mirror of
https://github.com/gosticks/DefinitelyTyped.git
synced 2025-10-16 12:05:41 +00:00
[prop-types] Do not consider null without undefined as optional (#38015)
* Do not consider null without undefined as optional * Fix revealed errors in existing tests - Non-required types can be undefined or null, so typescript types must reflect this * Ensure undefined-ness is stored alongside nominalTypeHack * Fix undefined in nominalTypeHack in a better way * Add tests for nullable and undefinable types * Fix formatting mistake * Update downstream tests
This commit is contained in:
parent
2fa0eebeb7
commit
dfe10fbcbe
@ -12,9 +12,9 @@ function FuncComp() {
|
||||
return null;
|
||||
}
|
||||
|
||||
// $ExpectType Requireable<number | null>
|
||||
// $ExpectType Requireable<number | null | undefined>
|
||||
AirbnbPropTypes.and([PropTypes.number]);
|
||||
// $ExpectType Requireable<number | null>
|
||||
// $ExpectType Requireable<number | null | undefined>
|
||||
AirbnbPropTypes.and([PropTypes.number, AirbnbPropTypes.nonNegativeInteger]);
|
||||
// $ExpectType Validator<number>
|
||||
AirbnbPropTypes.and([PropTypes.number, AirbnbPropTypes.integer()], 'foo').isRequired;
|
||||
@ -87,7 +87,7 @@ interface ForbidShape {
|
||||
baz?: boolean | null;
|
||||
}
|
||||
|
||||
// $ExpectType ValidationMap<{ foo: string | null; bar: number; baz: boolean | null; }>
|
||||
// $ExpectType ValidationMap<{ foo: string | null | undefined; bar: number; baz: boolean | null | undefined; }>
|
||||
AirbnbPropTypes.forbidExtraProps({
|
||||
foo: PropTypes.string,
|
||||
bar: PropTypes.number.isRequired,
|
||||
@ -155,7 +155,7 @@ AirbnbPropTypes.range<5>(0, 10);
|
||||
// $ExpectType Requireable<ReactLegacyRefLike<HTMLElement>>
|
||||
AirbnbPropTypes.ref();
|
||||
|
||||
// $ExpectType Requireable<string | null>
|
||||
// $ExpectType Requireable<string | null | undefined>
|
||||
AirbnbPropTypes.requiredBy('foo', PropTypes.string);
|
||||
// $ExpectType Validator<number>
|
||||
AirbnbPropTypes.requiredBy('bar', PropTypes.number, 42).isRequired;
|
||||
@ -177,11 +177,11 @@ interface ShapeShape {
|
||||
bar?: number | null;
|
||||
}
|
||||
|
||||
// $ExpectType Requireable<{ foo: string | null; }>
|
||||
// $ExpectType Requireable<{ foo: string | null | undefined; }>
|
||||
AirbnbPropTypes.shape({
|
||||
foo: PropTypes.string,
|
||||
});
|
||||
// $ExpectType Requireable<{ foo: string | null; bar: number | null; }>
|
||||
// $ExpectType Requireable<{ foo: string | null | undefined; bar: number | null | undefined; }>
|
||||
AirbnbPropTypes.shape({
|
||||
foo: PropTypes.string,
|
||||
bar: PropTypes.number,
|
||||
@ -200,5 +200,5 @@ AirbnbPropTypes.uniqueArray();
|
||||
// $ExpectType Requireable<string[]>
|
||||
AirbnbPropTypes.uniqueArray<string>();
|
||||
|
||||
// $ExpectType Requireable<{ [key: string]: number | null; }>
|
||||
// $ExpectType Requireable<{ [key: string]: number | null | undefined; }>
|
||||
AirbnbPropTypes.valuesOf(PropTypes.number);
|
||||
|
||||
6
types/prop-types/index.d.ts
vendored
6
types/prop-types/index.d.ts
vendored
@ -31,7 +31,7 @@ export type ReactNodeLike =
|
||||
|
||||
export const nominalTypeHack: unique symbol;
|
||||
|
||||
export type IsOptional<T> = undefined | null extends T ? true : undefined extends T ? true : null extends T ? true : false;
|
||||
export type IsOptional<T> = undefined extends T ? true : false;
|
||||
|
||||
export type RequiredKeys<V> = { [K in keyof V]-?: Exclude<V[K], undefined> extends Validator<infer T> ? IsOptional<T> extends true ? never : K : never }[keyof V];
|
||||
export type OptionalKeys<V> = Exclude<keyof V, RequiredKeys<V>>;
|
||||
@ -39,7 +39,9 @@ export type InferPropsInner<V> = { [K in keyof V]-?: InferType<V[K]>; };
|
||||
|
||||
export interface Validator<T> {
|
||||
(props: object, propName: string, componentName: string, location: string, propFullName: string): Error | null;
|
||||
[nominalTypeHack]?: T;
|
||||
[nominalTypeHack]?: {
|
||||
type: T;
|
||||
};
|
||||
}
|
||||
|
||||
export interface Requireable<T> extends Validator<T | undefined | null> {
|
||||
|
||||
@ -20,7 +20,7 @@ interface Props {
|
||||
instanceOf: TestClass;
|
||||
oneOf: 'a' | 'b' | 'c';
|
||||
oneOfType: string | boolean | {
|
||||
foo?: string;
|
||||
foo?: string | null;
|
||||
bar: number;
|
||||
};
|
||||
numberOrFalse: false | number;
|
||||
@ -29,10 +29,12 @@ interface Props {
|
||||
objectOf: { [K: string]: number };
|
||||
shape: {
|
||||
foo: string;
|
||||
bar?: boolean;
|
||||
baz?: any
|
||||
bar?: boolean | null;
|
||||
baz?: any;
|
||||
};
|
||||
optionalNumber?: number | null;
|
||||
nullableNumber: number | null;
|
||||
undefinableNumber?: number;
|
||||
customProp?: typeof uniqueType;
|
||||
component: PropTypes.ReactComponentLike;
|
||||
}
|
||||
@ -75,6 +77,8 @@ const propTypes: PropTypesMap = {
|
||||
objectOf: PropTypes.objectOf(PropTypes.number.isRequired).isRequired,
|
||||
shape: PropTypes.shape(innerProps).isRequired,
|
||||
optionalNumber: PropTypes.number,
|
||||
nullableNumber: (() => null) as PropTypes.Validator<number | null>,
|
||||
undefinableNumber: (() => null) as PropTypes.Validator<number | undefined>,
|
||||
customProp: (() => null) as PropTypes.Validator<typeof uniqueType | undefined>,
|
||||
component: PropTypes.elementType.isRequired
|
||||
};
|
||||
@ -102,6 +106,8 @@ const propTypesWithoutAnnotation = {
|
||||
objectOf: PropTypes.objectOf(PropTypes.number.isRequired).isRequired,
|
||||
shape: PropTypes.shape(innerProps).isRequired,
|
||||
optionalNumber: PropTypes.number,
|
||||
nullableNumber: (() => null) as PropTypes.Validator<number | null>,
|
||||
undefinableNumber: (() => null) as PropTypes.Validator<number | undefined>,
|
||||
customProp: (() => null) as PropTypes.Validator<typeof uniqueType | undefined>,
|
||||
component: PropTypes.elementType.isRequired
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user