Skip to content

Commit

Permalink
[core] In typescript-to-proptypes, respect the value pass to the ge…
Browse files Browse the repository at this point in the history
…neric (mui#34311)
  • Loading branch information
flaviendelangle authored and Daniel Rabe committed Nov 29, 2022
1 parent a278ee4 commit b0a25c2
Show file tree
Hide file tree
Showing 7 changed files with 54 additions and 22 deletions.
Expand Up @@ -56,7 +56,7 @@ Button.propTypes = {
root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
}),
defaultListboxOpen: PropTypes.bool,
defaultValue: PropTypes.object,
defaultValue: PropTypes.any,
disabled: PropTypes.bool.isRequired,
focusVisible: PropTypes.bool.isRequired,
getSerializedValue: PropTypes.func,
Expand All @@ -68,7 +68,7 @@ Button.propTypes = {
open: PropTypes.bool.isRequired,
optionStringifier: PropTypes.func,
renderValue: PropTypes.func,
value: PropTypes.object,
value: PropTypes.any,
}).isRequired,
};

Expand Down
4 changes: 2 additions & 2 deletions packages/mui-base/src/SelectUnstyled/SelectUnstyled.tsx
Expand Up @@ -278,7 +278,7 @@ SelectUnstyled.propTypes /* remove-proptypes */ = {
/**
* The default selected value. Use when the component is not controlled.
*/
defaultValue: PropTypes /* @typescript-to-proptypes-ignore */.any,
defaultValue: PropTypes.any,
/**
* If `true`, the select is disabled.
* @default false
Expand Down Expand Up @@ -330,7 +330,7 @@ SelectUnstyled.propTypes /* remove-proptypes */ = {
* The selected value.
* Set to `null` to deselect all options.
*/
value: PropTypes /* @typescript-to-proptypes-ignore */.any,
value: PropTypes.any,
} as any;

export default SelectUnstyled;
4 changes: 2 additions & 2 deletions packages/mui-joy/src/Select/Select.tsx
Expand Up @@ -610,7 +610,7 @@ Select.propTypes /* remove-proptypes */ = {
/**
* The default selected value. Use when the component is not controlled.
*/
defaultValue: PropTypes /* @typescript-to-proptypes-ignore */.any,
defaultValue: PropTypes.any,
/**
* If `true`, the component is disabled.
* @default false
Expand Down Expand Up @@ -666,7 +666,7 @@ Select.propTypes /* remove-proptypes */ = {
* The selected value.
* Set to `null` to deselect all options.
*/
value: PropTypes /* @typescript-to-proptypes-ignore */.any,
value: PropTypes.any,
/**
* The variant to use.
* @default 'solid'
Expand Down
2 changes: 1 addition & 1 deletion packages/mui-material/src/CardHeader/CardHeader.js
Expand Up @@ -176,7 +176,7 @@ CardHeader.propTypes /* remove-proptypes */ = {
* The component used for the root node.
* Either a string to use a HTML element or a component.
*/
component: PropTypes /* @typescript-to-proptypes-ignore */.elementType,
component: PropTypes.elementType,
/**
* If `true`, `subheader` and `title` won't be wrapped by a Typography component.
* This can be useful to render an alternative Typography variant by wrapping
Expand Down
48 changes: 33 additions & 15 deletions packages/typescript-to-proptypes/src/parser.ts
Expand Up @@ -186,12 +186,6 @@ export function parseFromProgram(
return t.createObjectType({ jsDoc: undefined });
}

const defaultGenericType = type.getDefault();
// This is generic type – use default type <T = SomeDefaultType>
if (defaultGenericType) {
return checkType(defaultGenericType, location, typeStack, name);
}

{
const typeNode = type as any;

Expand Down Expand Up @@ -269,6 +263,21 @@ export function parseFromProgram(
return node.types.length === 1 ? node.types[0] : node;
}

if (type.flags & ts.TypeFlags.TypeParameter) {
const baseConstraintOfType = checker.getBaseConstraintOfType(type);

if (baseConstraintOfType) {
if (
baseConstraintOfType.flags & ts.TypeFlags.Object &&
baseConstraintOfType.symbol.members?.size === 0
) {
return t.createAnyType({ jsDoc: getDocumentation(type.symbol) });
}

return checkType(baseConstraintOfType!, location, typeStack, name);
}
}

if (type.flags & ts.TypeFlags.String) {
return t.createStringType({ jsDoc: getDocumentation(type.symbol) });
}
Expand Down Expand Up @@ -421,15 +430,24 @@ export function parseFromProgram(
// so we just pick the first and ignore the rest
checker.getTypeOfSymbolAtLocation(symbol, declaration)
: checker.getTypeOfSymbolAtLocation(symbol, location);
// get `React.ElementType` from `C extends React.ElementType`
const declaredType =
declaration !== undefined ? checker.getTypeAtLocation(declaration) : undefined;
const baseConstraintOfType =
declaredType !== undefined ? checker.getBaseConstraintOfType(declaredType) : undefined;
const type =
baseConstraintOfType !== undefined && baseConstraintOfType !== declaredType
? baseConstraintOfType
: symbolType;

let type: ts.Type;
if (declaration === undefined) {
type = symbolType;
} else {
const declaredType = checker.getTypeAtLocation(declaration);
const baseConstraintOfType = checker.getBaseConstraintOfType(declaredType);

if (baseConstraintOfType === undefined || baseConstraintOfType === declaredType) {
type = symbolType;
}
// get `React.ElementType` from `C extends React.ElementType`
else if (baseConstraintOfType.aliasSymbol?.escapedName === 'ElementType') {
type = baseConstraintOfType;
} else {
type = symbolType;
}
}

if (!type) {
throw new Error('No types found');
Expand Down
10 changes: 10 additions & 0 deletions packages/typescript-to-proptypes/test/generic/input.d.ts
@@ -0,0 +1,10 @@
type Type = 'one' | 'two' | 'three'

interface ParentProps<T extends Type> {
optionalType?: T;
requiredType: T
}

interface ChildProps extends ParentProps<'one' | 'two'> {}

export function Foo(props: ChildProps): JSX.Element;
4 changes: 4 additions & 0 deletions packages/typescript-to-proptypes/test/generic/output.js
@@ -0,0 +1,4 @@
Foo.propTypes = {
optionalType: PropTypes.oneOf(['one', 'two']),
requiredType: PropTypes.oneOf(['one', 'two']).isRequired,
};

0 comments on commit b0a25c2

Please sign in to comment.