From 66ea5a038f6cd3e392e2a8e81c4b9a76b7870394 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 15 Jan 2019 16:52:00 -0800 Subject: [PATCH 01/13] Introduce simpliciations for extract/exclude-like conditional types and fix restrictive instantiations --- src/compiler/checker.ts | 32 ++++++++++++++++--- .../baselines/reference/conditionalTypes1.js | 2 ++ .../reference/conditionalTypes1.types | 14 ++++---- .../reference/conditionalTypes2.types | 6 ++-- ...erIndirectlyConstrainedToItself.errors.txt | 5 +-- ...rameterIndirectlyConstrainedToItself.types | 2 +- 6 files changed, 42 insertions(+), 19 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b48ba6bbcd684..27f91b02ed4fe 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -9982,6 +9982,26 @@ namespace ts { if (checkType === wildcardType || extendsType === wildcardType) { return wildcardType; } + // Simplifications for types of the form `T extends U ? U : never` and `T extends U ? never : T`. + const originalCheckType = getActualTypeVariable(root.checkType); + const trueType = instantiateType(root.trueType, mapper); + const falseType = instantiateType(root.falseType, mapper); + if (falseType.flags & TypeFlags.Never && getActualTypeVariable(root.trueType) === originalCheckType) { + if (isTypeAssignableTo(getRestrictiveInstantiation(checkType), getRestrictiveInstantiation(extendsType))) { // Always true + return getIntersectionType([trueType, extendsType]); + } + else if (getUnionType([getIntersectionType([checkType, extendsType]), neverType]).flags & TypeFlags.Never) { // Always false + return neverType; + } + } + else if (trueType.flags & TypeFlags.Never && getActualTypeVariable(root.falseType) === originalCheckType) { + if (isTypeAssignableTo(getRestrictiveInstantiation(checkType), getRestrictiveInstantiation(extendsType))) { // Always true + return neverType; + } + else if (getUnionType([getIntersectionType([checkType, extendsType]), neverType]).flags & TypeFlags.Never) { // Always false + return falseType; // TODO: Intersect negated `extends` type here + } + } const checkTypeInstantiable = maybeTypeOfKind(checkType, TypeFlags.Instantiable | TypeFlags.GenericMappedType); let combinedMapper: TypeMapper | undefined; if (root.inferTypeParameters) { @@ -9999,18 +10019,18 @@ namespace ts { // We attempt to resolve the conditional type only when the check and extends types are non-generic if (!checkTypeInstantiable && !maybeTypeOfKind(inferredExtendsType, TypeFlags.Instantiable | TypeFlags.GenericMappedType)) { if (inferredExtendsType.flags & TypeFlags.AnyOrUnknown) { - return instantiateType(root.trueType, mapper); + return trueType; } // Return union of trueType and falseType for 'any' since it matches anything if (checkType.flags & TypeFlags.Any) { - return getUnionType([instantiateType(root.trueType, combinedMapper || mapper), instantiateType(root.falseType, mapper)]); + return getUnionType([instantiateType(root.trueType, combinedMapper || mapper), falseType]); } // Return falseType for a definitely false extends check. We check an instantations of the two // types with type parameters mapped to the wildcard type, the most permissive instantiations // possible (the wildcard type is assignable to and from all types). If those are not related, // then no instatiations will be and we can just return the false branch type. if (!isTypeAssignableTo(getPermissiveInstantiation(checkType), getPermissiveInstantiation(inferredExtendsType))) { - return instantiateType(root.falseType, mapper); + return falseType; } // Return trueType for a definitely true extends check. We check instantiations of the two // types with type parameters mapped to their restrictive form, i.e. a form of the type parameter @@ -10628,7 +10648,11 @@ namespace ts { } function getRestrictiveTypeParameter(tp: TypeParameter) { - return !tp.constraint ? tp : tp.restrictiveInstantiation || (tp.restrictiveInstantiation = createTypeParameter(tp.symbol)); + return tp.constraint === noConstraintType ? tp : tp.restrictiveInstantiation || ( + tp.restrictiveInstantiation = createTypeParameter(tp.symbol), + (tp.restrictiveInstantiation as TypeParameter).constraint = unknownType, + tp.restrictiveInstantiation + ); } function restrictiveMapper(type: Type) { diff --git a/tests/baselines/reference/conditionalTypes1.js b/tests/baselines/reference/conditionalTypes1.js index 7ce19ce17e70c..11f74e488caa6 100644 --- a/tests/baselines/reference/conditionalTypes1.js +++ b/tests/baselines/reference/conditionalTypes1.js @@ -510,6 +510,8 @@ declare function f5(p: K): Extract = Extract void), Function>; // string | numbe >T02 : string | number type T03 = Extract void), Function>; // () => void ->T03 : () => void +>T03 : (() => void) & Function type T04 = NonNullable; // string | number >T04 : string | number @@ -113,7 +113,7 @@ type T10 = Exclude; // { k: "c", c: boolean } >k : "a" | "b" type T11 = Extract; // { k: "a", a: number } | { k: "b", b: string } ->T11 : { k: "a"; a: number; } | { k: "b"; b: string; } +>T11 : ({ k: "a"; a: number; } & { k: "a" | "b"; }) | ({ k: "b"; b: string; } & { k: "a" | "b"; }) >k : "a" | "b" type T12 = Exclude; // { k: "c", c: boolean } @@ -122,7 +122,7 @@ type T12 = Exclude; // { k: "c", c: boolean } >k : "b" type T13 = Extract; // { k: "a", a: number } | { k: "b", b: string } ->T13 : { k: "a"; a: number; } | { k: "b"; b: string; } +>T13 : ({ k: "a"; a: number; } & { k: "a"; }) | ({ k: "a"; a: number; } & { k: "b"; }) | ({ k: "b"; b: string; } & { k: "a"; }) | ({ k: "b"; b: string; } & { k: "b"; }) >k : "a" >k : "b" @@ -140,8 +140,8 @@ declare function f5(p: K): Extractk : K let x0 = f5("a"); // { k: "a", a: number } ->x0 : { k: "a"; a: number; } ->f5("a") : { k: "a"; a: number; } +>x0 : { k: "a"; a: number; } & { k: "a"; } +>f5("a") : { k: "a"; a: number; } & { k: "a"; } >f5 : (p: K) => Extract >"a" : "a" @@ -150,13 +150,13 @@ type OptionsOfKind = Extract; >k : K type T16 = OptionsOfKind<"a" | "b">; // { k: "a", a: number } | { k: "b", b: string } ->T16 : { k: "a"; a: number; } | { k: "b"; b: string; } +>T16 : ({ k: "a"; a: number; } & { k: "a" | "b"; }) | ({ k: "b"; b: string; } & { k: "a" | "b"; }) type Select = Extract; >Select : Extract type T17 = Select; // // { k: "a", a: number } | { k: "b", b: string } ->T17 : { k: "a"; a: number; } | { k: "b"; b: string; } +>T17 : ({ k: "a"; a: number; } & { k: "a" | "b"; }) | ({ k: "b"; b: string; } & { k: "a" | "b"; }) type TypeName = >TypeName : TypeName diff --git a/tests/baselines/reference/conditionalTypes2.types b/tests/baselines/reference/conditionalTypes2.types index aac6ba47475c6..7226d6b93af68 100644 --- a/tests/baselines/reference/conditionalTypes2.types +++ b/tests/baselines/reference/conditionalTypes2.types @@ -130,14 +130,14 @@ function f12(x: string | (() => string) | undefined) { >x : string | (() => string) | undefined const f = getFunction(x); // () => string ->f : () => string ->getFunction(x) : () => string +>f : (() => string) & Function +>getFunction(x) : (() => string) & Function >getFunction : (item: T) => Extract >x : string | (() => string) | undefined f(); >f() : string ->f : () => string +>f : (() => string) & Function } type Foo = { foo: string }; diff --git a/tests/baselines/reference/typeParameterIndirectlyConstrainedToItself.errors.txt b/tests/baselines/reference/typeParameterIndirectlyConstrainedToItself.errors.txt index 8ca5c51ea2dd4..4dcf8530b019d 100644 --- a/tests/baselines/reference/typeParameterIndirectlyConstrainedToItself.errors.txt +++ b/tests/baselines/reference/typeParameterIndirectlyConstrainedToItself.errors.txt @@ -25,10 +25,9 @@ tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterInd tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterIndirectlyConstrainedToItself.ts(16,47): error TS2313: Type parameter 'V' has a circular constraint. tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterIndirectlyConstrainedToItself.ts(18,32): error TS2313: Type parameter 'T' has a circular constraint. tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterIndirectlyConstrainedToItself.ts(18,45): error TS2313: Type parameter 'V' has a circular constraint. -tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterIndirectlyConstrainedToItself.ts(23,24): error TS2313: Type parameter 'S' has a circular constraint. -==== tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterIndirectlyConstrainedToItself.ts (28 errors) ==== +==== tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterIndirectlyConstrainedToItself.ts (27 errors) ==== class C { } ~ !!! error TS2313: Type parameter 'U' has a circular constraint. @@ -106,6 +105,4 @@ tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterInd type Foo = [T] extends [number] ? {} : {}; function foo>() {} - ~~~~~~ -!!! error TS2313: Type parameter 'S' has a circular constraint. \ No newline at end of file diff --git a/tests/baselines/reference/typeParameterIndirectlyConstrainedToItself.types b/tests/baselines/reference/typeParameterIndirectlyConstrainedToItself.types index c2b20688fb5cb..637cb583d2e62 100644 --- a/tests/baselines/reference/typeParameterIndirectlyConstrainedToItself.types +++ b/tests/baselines/reference/typeParameterIndirectlyConstrainedToItself.types @@ -38,5 +38,5 @@ type Foo = [T] extends [number] ? {} : {}; >Foo : Foo function foo>() {} ->foo : () => void +>foo : >() => void From d3c02d1384976f7466de8c2b889cd5eee2d3d03d Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 15 Jan 2019 17:12:38 -0800 Subject: [PATCH 02/13] Add test for the common simplifications --- .../conditionalTypesSimplifyWhenTrivial.js | 32 +++++++++ ...onditionalTypesSimplifyWhenTrivial.symbols | 67 +++++++++++++++++++ .../conditionalTypesSimplifyWhenTrivial.types | 49 ++++++++++++++ .../conditionalTypesSimplifyWhenTrivial.ts | 18 +++++ 4 files changed, 166 insertions(+) create mode 100644 tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.js create mode 100644 tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.symbols create mode 100644 tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.types create mode 100644 tests/cases/compiler/conditionalTypesSimplifyWhenTrivial.ts diff --git a/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.js b/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.js new file mode 100644 index 0000000000000..988e2e5912fc7 --- /dev/null +++ b/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.js @@ -0,0 +1,32 @@ +//// [conditionalTypesSimplifyWhenTrivial.ts] +const fn1 = ( + params: Pick>, +): Params => params; + +function fn2(x: Exclude) { + var y: T = x; + x = y; +} + +const fn3 = ( + params: Pick>, +): Params => params; + +function fn4(x: Extract) { + var y: T = x; + x = y; +} + + +//// [conditionalTypesSimplifyWhenTrivial.js] +"use strict"; +var fn1 = function (params) { return params; }; +function fn2(x) { + var y = x; + x = y; +} +var fn3 = function (params) { return params; }; +function fn4(x) { + var y = x; + x = y; +} diff --git a/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.symbols b/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.symbols new file mode 100644 index 0000000000000..1191ca75ce72b --- /dev/null +++ b/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.symbols @@ -0,0 +1,67 @@ +=== tests/cases/compiler/conditionalTypesSimplifyWhenTrivial.ts === +const fn1 = ( +>fn1 : Symbol(fn1, Decl(conditionalTypesSimplifyWhenTrivial.ts, 0, 5)) +>Params : Symbol(Params, Decl(conditionalTypesSimplifyWhenTrivial.ts, 0, 13)) + + params: Pick>, +>params : Symbol(params, Decl(conditionalTypesSimplifyWhenTrivial.ts, 0, 21)) +>Pick : Symbol(Pick, Decl(lib.es5.d.ts, --, --)) +>Params : Symbol(Params, Decl(conditionalTypesSimplifyWhenTrivial.ts, 0, 13)) +>Exclude : Symbol(Exclude, Decl(lib.es5.d.ts, --, --)) +>Params : Symbol(Params, Decl(conditionalTypesSimplifyWhenTrivial.ts, 0, 13)) + +): Params => params; +>Params : Symbol(Params, Decl(conditionalTypesSimplifyWhenTrivial.ts, 0, 13)) +>params : Symbol(params, Decl(conditionalTypesSimplifyWhenTrivial.ts, 0, 21)) + +function fn2(x: Exclude) { +>fn2 : Symbol(fn2, Decl(conditionalTypesSimplifyWhenTrivial.ts, 2, 20)) +>T : Symbol(T, Decl(conditionalTypesSimplifyWhenTrivial.ts, 4, 13)) +>x : Symbol(x, Decl(conditionalTypesSimplifyWhenTrivial.ts, 4, 16)) +>Exclude : Symbol(Exclude, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(conditionalTypesSimplifyWhenTrivial.ts, 4, 13)) + + var y: T = x; +>y : Symbol(y, Decl(conditionalTypesSimplifyWhenTrivial.ts, 5, 7)) +>T : Symbol(T, Decl(conditionalTypesSimplifyWhenTrivial.ts, 4, 13)) +>x : Symbol(x, Decl(conditionalTypesSimplifyWhenTrivial.ts, 4, 16)) + + x = y; +>x : Symbol(x, Decl(conditionalTypesSimplifyWhenTrivial.ts, 4, 16)) +>y : Symbol(y, Decl(conditionalTypesSimplifyWhenTrivial.ts, 5, 7)) +} + +const fn3 = ( +>fn3 : Symbol(fn3, Decl(conditionalTypesSimplifyWhenTrivial.ts, 9, 5)) +>Params : Symbol(Params, Decl(conditionalTypesSimplifyWhenTrivial.ts, 9, 13)) + + params: Pick>, +>params : Symbol(params, Decl(conditionalTypesSimplifyWhenTrivial.ts, 9, 21)) +>Pick : Symbol(Pick, Decl(lib.es5.d.ts, --, --)) +>Params : Symbol(Params, Decl(conditionalTypesSimplifyWhenTrivial.ts, 9, 13)) +>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --)) +>Params : Symbol(Params, Decl(conditionalTypesSimplifyWhenTrivial.ts, 9, 13)) +>Params : Symbol(Params, Decl(conditionalTypesSimplifyWhenTrivial.ts, 9, 13)) + +): Params => params; +>Params : Symbol(Params, Decl(conditionalTypesSimplifyWhenTrivial.ts, 9, 13)) +>params : Symbol(params, Decl(conditionalTypesSimplifyWhenTrivial.ts, 9, 21)) + +function fn4(x: Extract) { +>fn4 : Symbol(fn4, Decl(conditionalTypesSimplifyWhenTrivial.ts, 11, 20)) +>T : Symbol(T, Decl(conditionalTypesSimplifyWhenTrivial.ts, 13, 13)) +>x : Symbol(x, Decl(conditionalTypesSimplifyWhenTrivial.ts, 13, 16)) +>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(conditionalTypesSimplifyWhenTrivial.ts, 13, 13)) +>T : Symbol(T, Decl(conditionalTypesSimplifyWhenTrivial.ts, 13, 13)) + + var y: T = x; +>y : Symbol(y, Decl(conditionalTypesSimplifyWhenTrivial.ts, 14, 7)) +>T : Symbol(T, Decl(conditionalTypesSimplifyWhenTrivial.ts, 13, 13)) +>x : Symbol(x, Decl(conditionalTypesSimplifyWhenTrivial.ts, 13, 16)) + + x = y; +>x : Symbol(x, Decl(conditionalTypesSimplifyWhenTrivial.ts, 13, 16)) +>y : Symbol(y, Decl(conditionalTypesSimplifyWhenTrivial.ts, 14, 7)) +} + diff --git a/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.types b/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.types new file mode 100644 index 0000000000000..f0f7ceffc2670 --- /dev/null +++ b/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.types @@ -0,0 +1,49 @@ +=== tests/cases/compiler/conditionalTypesSimplifyWhenTrivial.ts === +const fn1 = ( +>fn1 : (params: Pick) => Params +>( params: Pick>,): Params => params : (params: Pick) => Params + + params: Pick>, +>params : Pick + +): Params => params; +>params : Pick + +function fn2(x: Exclude) { +>fn2 : (x: T) => void +>x : T + + var y: T = x; +>y : T +>x : T + + x = y; +>x = y : T +>x : T +>y : T +} + +const fn3 = ( +>fn3 : (params: Pick) => Params +>( params: Pick>,): Params => params : (params: Pick) => Params + + params: Pick>, +>params : Pick + +): Params => params; +>params : Pick + +function fn4(x: Extract) { +>fn4 : (x: T) => void +>x : T + + var y: T = x; +>y : T +>x : T + + x = y; +>x = y : T +>x : T +>y : T +} + diff --git a/tests/cases/compiler/conditionalTypesSimplifyWhenTrivial.ts b/tests/cases/compiler/conditionalTypesSimplifyWhenTrivial.ts new file mode 100644 index 0000000000000..ff1a946d38ac4 --- /dev/null +++ b/tests/cases/compiler/conditionalTypesSimplifyWhenTrivial.ts @@ -0,0 +1,18 @@ +// @strict: true +const fn1 = ( + params: Pick>, +): Params => params; + +function fn2(x: Exclude) { + var y: T = x; + x = y; +} + +const fn3 = ( + params: Pick>, +): Params => params; + +function fn4(x: Extract) { + var y: T = x; + x = y; +} From 5dc678950355f46d7ee6399ebc5dba1fd1808d19 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 16 Jan 2019 10:34:52 -0800 Subject: [PATCH 03/13] unify true branch constraint generation logic and true branch simplification --- src/compiler/checker.ts | 22 +++-- .../baselines/reference/conditionalTypes1.js | 2 +- .../reference/conditionalTypes1.types | 14 +-- .../reference/conditionalTypes2.types | 6 +- .../conditionalTypesSimplifyWhenTrivial.js | 35 +++++++- ...onditionalTypesSimplifyWhenTrivial.symbols | 89 +++++++++++++++++++ .../conditionalTypesSimplifyWhenTrivial.types | 56 ++++++++++++ .../conditionalTypesSimplifyWhenTrivial.ts | 24 +++++ 8 files changed, 227 insertions(+), 21 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 27f91b02ed4fe..b9c44ca42e138 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7402,15 +7402,19 @@ namespace ts { return baseConstraint && baseConstraint !== type ? baseConstraint : undefined; } + function getDefaultConstraintOfTrueBranchOfConditionalType(root: ConditionalRoot, combinedMapper: TypeMapper | undefined, mapper: TypeMapper | undefined) { + const rootTrueType = root.trueType; + const rootTrueConstraint = !(rootTrueType.flags & TypeFlags.Substitution) + ? rootTrueType + : instantiateType(((rootTrueType).substitute), combinedMapper || mapper).flags & TypeFlags.AnyOrUnknown + ? (rootTrueType).typeVariable + : getIntersectionType([(rootTrueType).substitute, (rootTrueType).typeVariable]); + return instantiateType(rootTrueConstraint, combinedMapper || mapper); + } + function getDefaultConstraintOfConditionalType(type: ConditionalType) { if (!type.resolvedDefaultConstraint) { - const rootTrueType = type.root.trueType; - const rootTrueConstraint = !(rootTrueType.flags & TypeFlags.Substitution) - ? rootTrueType - : ((rootTrueType).substitute).flags & TypeFlags.AnyOrUnknown - ? (rootTrueType).typeVariable - : getIntersectionType([(rootTrueType).substitute, (rootTrueType).typeVariable]); - type.resolvedDefaultConstraint = getUnionType([instantiateType(rootTrueConstraint, type.combinedMapper || type.mapper), getFalseTypeFromConditionalType(type)]); + type.resolvedDefaultConstraint = getUnionType([getDefaultConstraintOfTrueBranchOfConditionalType(type.root, type.combinedMapper, type.mapper), getFalseTypeFromConditionalType(type)]); } return type.resolvedDefaultConstraint; } @@ -9982,13 +9986,13 @@ namespace ts { if (checkType === wildcardType || extendsType === wildcardType) { return wildcardType; } - // Simplifications for types of the form `T extends U ? U : never` and `T extends U ? never : T`. + // Simplifications for types of the form `T extends U ? T : never` and `T extends U ? never : T`. const originalCheckType = getActualTypeVariable(root.checkType); const trueType = instantiateType(root.trueType, mapper); const falseType = instantiateType(root.falseType, mapper); if (falseType.flags & TypeFlags.Never && getActualTypeVariable(root.trueType) === originalCheckType) { if (isTypeAssignableTo(getRestrictiveInstantiation(checkType), getRestrictiveInstantiation(extendsType))) { // Always true - return getIntersectionType([trueType, extendsType]); + return getDefaultConstraintOfTrueBranchOfConditionalType(root, /*combinedMapper*/ undefined, mapper); } else if (getUnionType([getIntersectionType([checkType, extendsType]), neverType]).flags & TypeFlags.Never) { // Always false return neverType; diff --git a/tests/baselines/reference/conditionalTypes1.js b/tests/baselines/reference/conditionalTypes1.js index 11f74e488caa6..34530db9fa309 100644 --- a/tests/baselines/reference/conditionalTypes1.js +++ b/tests/baselines/reference/conditionalTypes1.js @@ -509,9 +509,9 @@ declare function f5(p: K): Extract; declare let x0: { k: "a"; - a: number; } & { k: "a"; + a: number; }; declare type OptionsOfKind = Extract void), Function>; // string | numbe >T02 : string | number type T03 = Extract void), Function>; // () => void ->T03 : (() => void) & Function +>T03 : Function & (() => void) type T04 = NonNullable; // string | number >T04 : string | number @@ -113,7 +113,7 @@ type T10 = Exclude; // { k: "c", c: boolean } >k : "a" | "b" type T11 = Extract; // { k: "a", a: number } | { k: "b", b: string } ->T11 : ({ k: "a"; a: number; } & { k: "a" | "b"; }) | ({ k: "b"; b: string; } & { k: "a" | "b"; }) +>T11 : ({ k: "a" | "b"; } & { k: "a"; a: number; }) | ({ k: "a" | "b"; } & { k: "b"; b: string; }) >k : "a" | "b" type T12 = Exclude; // { k: "c", c: boolean } @@ -122,7 +122,7 @@ type T12 = Exclude; // { k: "c", c: boolean } >k : "b" type T13 = Extract; // { k: "a", a: number } | { k: "b", b: string } ->T13 : ({ k: "a"; a: number; } & { k: "a"; }) | ({ k: "a"; a: number; } & { k: "b"; }) | ({ k: "b"; b: string; } & { k: "a"; }) | ({ k: "b"; b: string; } & { k: "b"; }) +>T13 : ({ k: "a"; } & { k: "a"; a: number; }) | ({ k: "b"; } & { k: "a"; a: number; }) | ({ k: "a"; } & { k: "b"; b: string; }) | ({ k: "b"; } & { k: "b"; b: string; }) >k : "a" >k : "b" @@ -140,8 +140,8 @@ declare function f5(p: K): Extractk : K let x0 = f5("a"); // { k: "a", a: number } ->x0 : { k: "a"; a: number; } & { k: "a"; } ->f5("a") : { k: "a"; a: number; } & { k: "a"; } +>x0 : { k: "a"; } & { k: "a"; a: number; } +>f5("a") : { k: "a"; } & { k: "a"; a: number; } >f5 : (p: K) => Extract >"a" : "a" @@ -150,13 +150,13 @@ type OptionsOfKind = Extract; >k : K type T16 = OptionsOfKind<"a" | "b">; // { k: "a", a: number } | { k: "b", b: string } ->T16 : ({ k: "a"; a: number; } & { k: "a" | "b"; }) | ({ k: "b"; b: string; } & { k: "a" | "b"; }) +>T16 : ({ k: "a" | "b"; } & { k: "a"; a: number; }) | ({ k: "a" | "b"; } & { k: "b"; b: string; }) type Select = Extract; >Select : Extract type T17 = Select; // // { k: "a", a: number } | { k: "b", b: string } ->T17 : ({ k: "a"; a: number; } & { k: "a" | "b"; }) | ({ k: "b"; b: string; } & { k: "a" | "b"; }) +>T17 : ({ k: "a" | "b"; } & { k: "a"; a: number; }) | ({ k: "a" | "b"; } & { k: "b"; b: string; }) type TypeName = >TypeName : TypeName diff --git a/tests/baselines/reference/conditionalTypes2.types b/tests/baselines/reference/conditionalTypes2.types index 7226d6b93af68..7113ccc2be809 100644 --- a/tests/baselines/reference/conditionalTypes2.types +++ b/tests/baselines/reference/conditionalTypes2.types @@ -130,14 +130,14 @@ function f12(x: string | (() => string) | undefined) { >x : string | (() => string) | undefined const f = getFunction(x); // () => string ->f : (() => string) & Function ->getFunction(x) : (() => string) & Function +>f : Function & (() => string) +>getFunction(x) : Function & (() => string) >getFunction : (item: T) => Extract >x : string | (() => string) | undefined f(); >f() : string ->f : (() => string) & Function +>f : Function & (() => string) } type Foo = { foo: string }; diff --git a/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.js b/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.js index 988e2e5912fc7..0f21167c4af6d 100644 --- a/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.js +++ b/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.js @@ -16,7 +16,30 @@ function fn4(x: Extract) { var y: T = x; x = y; } - + +declare var x: Extract; // Should be `numebr | string` and not `any` + +type ExtractWithDefault = T extends U ? T : D; + +type ExcludeWithDefault = T extends U ? D : T; + +const fn5 = ( + params: Pick>, +): Params => params; + +function fn6(x: ExcludeWithDefault) { + var y: T = x; + x = y; +} + +const fn7 = ( + params: Pick>, +): Params => params; + +function fn8(x: ExtractWithDefault) { + var y: T = x; + x = y; +} //// [conditionalTypesSimplifyWhenTrivial.js] "use strict"; @@ -30,3 +53,13 @@ function fn4(x) { var y = x; x = y; } +var fn5 = function (params) { return params; }; +function fn6(x) { + var y = x; + x = y; +} +var fn7 = function (params) { return params; }; +function fn8(x) { + var y = x; + x = y; +} diff --git a/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.symbols b/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.symbols index 1191ca75ce72b..b000d39eb081d 100644 --- a/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.symbols +++ b/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.symbols @@ -65,3 +65,92 @@ function fn4(x: Extract) { >y : Symbol(y, Decl(conditionalTypesSimplifyWhenTrivial.ts, 14, 7)) } +declare var x: Extract; // Should be `numebr | string` and not `any` +>x : Symbol(x, Decl(conditionalTypesSimplifyWhenTrivial.ts, 18, 11)) +>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --)) + +type ExtractWithDefault = T extends U ? T : D; +>ExtractWithDefault : Symbol(ExtractWithDefault, Decl(conditionalTypesSimplifyWhenTrivial.ts, 18, 45)) +>T : Symbol(T, Decl(conditionalTypesSimplifyWhenTrivial.ts, 20, 24)) +>U : Symbol(U, Decl(conditionalTypesSimplifyWhenTrivial.ts, 20, 26)) +>D : Symbol(D, Decl(conditionalTypesSimplifyWhenTrivial.ts, 20, 29)) +>T : Symbol(T, Decl(conditionalTypesSimplifyWhenTrivial.ts, 20, 24)) +>U : Symbol(U, Decl(conditionalTypesSimplifyWhenTrivial.ts, 20, 26)) +>T : Symbol(T, Decl(conditionalTypesSimplifyWhenTrivial.ts, 20, 24)) +>D : Symbol(D, Decl(conditionalTypesSimplifyWhenTrivial.ts, 20, 29)) + +type ExcludeWithDefault = T extends U ? D : T; +>ExcludeWithDefault : Symbol(ExcludeWithDefault, Decl(conditionalTypesSimplifyWhenTrivial.ts, 20, 63)) +>T : Symbol(T, Decl(conditionalTypesSimplifyWhenTrivial.ts, 22, 24)) +>U : Symbol(U, Decl(conditionalTypesSimplifyWhenTrivial.ts, 22, 26)) +>D : Symbol(D, Decl(conditionalTypesSimplifyWhenTrivial.ts, 22, 29)) +>T : Symbol(T, Decl(conditionalTypesSimplifyWhenTrivial.ts, 22, 24)) +>U : Symbol(U, Decl(conditionalTypesSimplifyWhenTrivial.ts, 22, 26)) +>D : Symbol(D, Decl(conditionalTypesSimplifyWhenTrivial.ts, 22, 29)) +>T : Symbol(T, Decl(conditionalTypesSimplifyWhenTrivial.ts, 22, 24)) + +const fn5 = ( +>fn5 : Symbol(fn5, Decl(conditionalTypesSimplifyWhenTrivial.ts, 24, 5)) +>Params : Symbol(Params, Decl(conditionalTypesSimplifyWhenTrivial.ts, 24, 13)) + + params: Pick>, +>params : Symbol(params, Decl(conditionalTypesSimplifyWhenTrivial.ts, 24, 21)) +>Pick : Symbol(Pick, Decl(lib.es5.d.ts, --, --)) +>Params : Symbol(Params, Decl(conditionalTypesSimplifyWhenTrivial.ts, 24, 13)) +>ExcludeWithDefault : Symbol(ExcludeWithDefault, Decl(conditionalTypesSimplifyWhenTrivial.ts, 20, 63)) +>Params : Symbol(Params, Decl(conditionalTypesSimplifyWhenTrivial.ts, 24, 13)) + +): Params => params; +>Params : Symbol(Params, Decl(conditionalTypesSimplifyWhenTrivial.ts, 24, 13)) +>params : Symbol(params, Decl(conditionalTypesSimplifyWhenTrivial.ts, 24, 21)) + +function fn6(x: ExcludeWithDefault) { +>fn6 : Symbol(fn6, Decl(conditionalTypesSimplifyWhenTrivial.ts, 26, 20)) +>T : Symbol(T, Decl(conditionalTypesSimplifyWhenTrivial.ts, 28, 13)) +>x : Symbol(x, Decl(conditionalTypesSimplifyWhenTrivial.ts, 28, 16)) +>ExcludeWithDefault : Symbol(ExcludeWithDefault, Decl(conditionalTypesSimplifyWhenTrivial.ts, 20, 63)) +>T : Symbol(T, Decl(conditionalTypesSimplifyWhenTrivial.ts, 28, 13)) + + var y: T = x; +>y : Symbol(y, Decl(conditionalTypesSimplifyWhenTrivial.ts, 29, 7)) +>T : Symbol(T, Decl(conditionalTypesSimplifyWhenTrivial.ts, 28, 13)) +>x : Symbol(x, Decl(conditionalTypesSimplifyWhenTrivial.ts, 28, 16)) + + x = y; +>x : Symbol(x, Decl(conditionalTypesSimplifyWhenTrivial.ts, 28, 16)) +>y : Symbol(y, Decl(conditionalTypesSimplifyWhenTrivial.ts, 29, 7)) +} + +const fn7 = ( +>fn7 : Symbol(fn7, Decl(conditionalTypesSimplifyWhenTrivial.ts, 33, 5)) +>Params : Symbol(Params, Decl(conditionalTypesSimplifyWhenTrivial.ts, 33, 13)) + + params: Pick>, +>params : Symbol(params, Decl(conditionalTypesSimplifyWhenTrivial.ts, 33, 21)) +>Pick : Symbol(Pick, Decl(lib.es5.d.ts, --, --)) +>Params : Symbol(Params, Decl(conditionalTypesSimplifyWhenTrivial.ts, 33, 13)) +>ExtractWithDefault : Symbol(ExtractWithDefault, Decl(conditionalTypesSimplifyWhenTrivial.ts, 18, 45)) +>Params : Symbol(Params, Decl(conditionalTypesSimplifyWhenTrivial.ts, 33, 13)) +>Params : Symbol(Params, Decl(conditionalTypesSimplifyWhenTrivial.ts, 33, 13)) + +): Params => params; +>Params : Symbol(Params, Decl(conditionalTypesSimplifyWhenTrivial.ts, 33, 13)) +>params : Symbol(params, Decl(conditionalTypesSimplifyWhenTrivial.ts, 33, 21)) + +function fn8(x: ExtractWithDefault) { +>fn8 : Symbol(fn8, Decl(conditionalTypesSimplifyWhenTrivial.ts, 35, 20)) +>T : Symbol(T, Decl(conditionalTypesSimplifyWhenTrivial.ts, 37, 13)) +>x : Symbol(x, Decl(conditionalTypesSimplifyWhenTrivial.ts, 37, 16)) +>ExtractWithDefault : Symbol(ExtractWithDefault, Decl(conditionalTypesSimplifyWhenTrivial.ts, 18, 45)) +>T : Symbol(T, Decl(conditionalTypesSimplifyWhenTrivial.ts, 37, 13)) +>T : Symbol(T, Decl(conditionalTypesSimplifyWhenTrivial.ts, 37, 13)) + + var y: T = x; +>y : Symbol(y, Decl(conditionalTypesSimplifyWhenTrivial.ts, 38, 7)) +>T : Symbol(T, Decl(conditionalTypesSimplifyWhenTrivial.ts, 37, 13)) +>x : Symbol(x, Decl(conditionalTypesSimplifyWhenTrivial.ts, 37, 16)) + + x = y; +>x : Symbol(x, Decl(conditionalTypesSimplifyWhenTrivial.ts, 37, 16)) +>y : Symbol(y, Decl(conditionalTypesSimplifyWhenTrivial.ts, 38, 7)) +} diff --git a/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.types b/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.types index f0f7ceffc2670..27ca13da91eba 100644 --- a/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.types +++ b/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.types @@ -47,3 +47,59 @@ function fn4(x: Extract) { >y : T } +declare var x: Extract; // Should be `numebr | string` and not `any` +>x : string | number + +type ExtractWithDefault = T extends U ? T : D; +>ExtractWithDefault : ExtractWithDefault + +type ExcludeWithDefault = T extends U ? D : T; +>ExcludeWithDefault : ExcludeWithDefault + +const fn5 = ( +>fn5 : (params: Pick) => Params +>( params: Pick>,): Params => params : (params: Pick) => Params + + params: Pick>, +>params : Pick + +): Params => params; +>params : Pick + +function fn6(x: ExcludeWithDefault) { +>fn6 : (x: T) => void +>x : T + + var y: T = x; +>y : T +>x : T + + x = y; +>x = y : T +>x : T +>y : T +} + +const fn7 = ( +>fn7 : (params: Pick) => Params +>( params: Pick>,): Params => params : (params: Pick) => Params + + params: Pick>, +>params : Pick + +): Params => params; +>params : Pick + +function fn8(x: ExtractWithDefault) { +>fn8 : (x: T) => void +>x : T + + var y: T = x; +>y : T +>x : T + + x = y; +>x = y : T +>x : T +>y : T +} diff --git a/tests/cases/compiler/conditionalTypesSimplifyWhenTrivial.ts b/tests/cases/compiler/conditionalTypesSimplifyWhenTrivial.ts index ff1a946d38ac4..34d3f215d1424 100644 --- a/tests/cases/compiler/conditionalTypesSimplifyWhenTrivial.ts +++ b/tests/cases/compiler/conditionalTypesSimplifyWhenTrivial.ts @@ -16,3 +16,27 @@ function fn4(x: Extract) { var y: T = x; x = y; } + +declare var x: Extract; // Should be `numebr | string` and not `any` + +type ExtractWithDefault = T extends U ? T : D; + +type ExcludeWithDefault = T extends U ? D : T; + +const fn5 = ( + params: Pick>, +): Params => params; + +function fn6(x: ExcludeWithDefault) { + var y: T = x; + x = y; +} + +const fn7 = ( + params: Pick>, +): Params => params; + +function fn8(x: ExtractWithDefault) { + var y: T = x; + x = y; +} \ No newline at end of file From ad4b7b2d74f593acc05cac178c2cb2025d5f3a41 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 16 Jan 2019 11:04:13 -0800 Subject: [PATCH 04/13] Use identical check on instantiated types --- src/compiler/checker.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b9c44ca42e138..e093338812a61 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -9987,10 +9987,9 @@ namespace ts { return wildcardType; } // Simplifications for types of the form `T extends U ? T : never` and `T extends U ? never : T`. - const originalCheckType = getActualTypeVariable(root.checkType); const trueType = instantiateType(root.trueType, mapper); const falseType = instantiateType(root.falseType, mapper); - if (falseType.flags & TypeFlags.Never && getActualTypeVariable(root.trueType) === originalCheckType) { + if (falseType.flags & TypeFlags.Never && isTypeIdenticalTo(getActualTypeVariable(trueType), getActualTypeVariable(checkType))) { if (isTypeAssignableTo(getRestrictiveInstantiation(checkType), getRestrictiveInstantiation(extendsType))) { // Always true return getDefaultConstraintOfTrueBranchOfConditionalType(root, /*combinedMapper*/ undefined, mapper); } @@ -9998,7 +9997,7 @@ namespace ts { return neverType; } } - else if (trueType.flags & TypeFlags.Never && getActualTypeVariable(root.falseType) === originalCheckType) { + else if (trueType.flags & TypeFlags.Never && isTypeIdenticalTo(getActualTypeVariable(falseType), getActualTypeVariable(checkType))) { if (isTypeAssignableTo(getRestrictiveInstantiation(checkType), getRestrictiveInstantiation(extendsType))) { // Always true return neverType; } From ca5530c1b9b536692533660f6495b56f0c9c268f Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 16 Jan 2019 11:15:26 -0800 Subject: [PATCH 05/13] Add late-instantiate conditionals to test --- .../conditionalTypesSimplifyWhenTrivial.js | 33 +++++++- ...onditionalTypesSimplifyWhenTrivial.symbols | 82 +++++++++++++++++++ .../conditionalTypesSimplifyWhenTrivial.types | 52 ++++++++++++ .../conditionalTypesSimplifyWhenTrivial.ts | 22 ++++- 4 files changed, 187 insertions(+), 2 deletions(-) diff --git a/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.js b/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.js index 0f21167c4af6d..84b243330d7e5 100644 --- a/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.js +++ b/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.js @@ -39,7 +39,28 @@ const fn7 = ( function fn8(x: ExtractWithDefault) { var y: T = x; x = y; -} +} + +type TemplatedConditional = TCheck extends TExtends ? TTrue : TFalse; + +const fn9 = ( + params: Pick>, +): Params => params; + +function fn10(x: TemplatedConditional) { + var y: T = x; + x = y; +} + +const fn11 = ( + params: Pick>, +): Params => params; + +function fn12(x: TemplatedConditional) { + var y: T = x; + x = y; +} + //// [conditionalTypesSimplifyWhenTrivial.js] "use strict"; @@ -63,3 +84,13 @@ function fn8(x) { var y = x; x = y; } +var fn9 = function (params) { return params; }; +function fn10(x) { + var y = x; + x = y; +} +var fn11 = function (params) { return params; }; +function fn12(x) { + var y = x; + x = y; +} diff --git a/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.symbols b/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.symbols index b000d39eb081d..248a91fd03a71 100644 --- a/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.symbols +++ b/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.symbols @@ -154,3 +154,85 @@ function fn8(x: ExtractWithDefault) { >x : Symbol(x, Decl(conditionalTypesSimplifyWhenTrivial.ts, 37, 16)) >y : Symbol(y, Decl(conditionalTypesSimplifyWhenTrivial.ts, 38, 7)) } + +type TemplatedConditional = TCheck extends TExtends ? TTrue : TFalse; +>TemplatedConditional : Symbol(TemplatedConditional, Decl(conditionalTypesSimplifyWhenTrivial.ts, 40, 1)) +>TCheck : Symbol(TCheck, Decl(conditionalTypesSimplifyWhenTrivial.ts, 42, 26)) +>TExtends : Symbol(TExtends, Decl(conditionalTypesSimplifyWhenTrivial.ts, 42, 33)) +>TTrue : Symbol(TTrue, Decl(conditionalTypesSimplifyWhenTrivial.ts, 42, 43)) +>TFalse : Symbol(TFalse, Decl(conditionalTypesSimplifyWhenTrivial.ts, 42, 50)) +>TCheck : Symbol(TCheck, Decl(conditionalTypesSimplifyWhenTrivial.ts, 42, 26)) +>TExtends : Symbol(TExtends, Decl(conditionalTypesSimplifyWhenTrivial.ts, 42, 33)) +>TTrue : Symbol(TTrue, Decl(conditionalTypesSimplifyWhenTrivial.ts, 42, 43)) +>TFalse : Symbol(TFalse, Decl(conditionalTypesSimplifyWhenTrivial.ts, 42, 50)) + +const fn9 = ( +>fn9 : Symbol(fn9, Decl(conditionalTypesSimplifyWhenTrivial.ts, 44, 5)) +>Params : Symbol(Params, Decl(conditionalTypesSimplifyWhenTrivial.ts, 44, 13)) + + params: Pick>, +>params : Symbol(params, Decl(conditionalTypesSimplifyWhenTrivial.ts, 44, 21)) +>Pick : Symbol(Pick, Decl(lib.es5.d.ts, --, --)) +>Params : Symbol(Params, Decl(conditionalTypesSimplifyWhenTrivial.ts, 44, 13)) +>TemplatedConditional : Symbol(TemplatedConditional, Decl(conditionalTypesSimplifyWhenTrivial.ts, 40, 1)) +>Params : Symbol(Params, Decl(conditionalTypesSimplifyWhenTrivial.ts, 44, 13)) +>Params : Symbol(Params, Decl(conditionalTypesSimplifyWhenTrivial.ts, 44, 13)) + +): Params => params; +>Params : Symbol(Params, Decl(conditionalTypesSimplifyWhenTrivial.ts, 44, 13)) +>params : Symbol(params, Decl(conditionalTypesSimplifyWhenTrivial.ts, 44, 21)) + +function fn10(x: TemplatedConditional) { +>fn10 : Symbol(fn10, Decl(conditionalTypesSimplifyWhenTrivial.ts, 46, 20)) +>T : Symbol(T, Decl(conditionalTypesSimplifyWhenTrivial.ts, 48, 14)) +>x : Symbol(x, Decl(conditionalTypesSimplifyWhenTrivial.ts, 48, 17)) +>TemplatedConditional : Symbol(TemplatedConditional, Decl(conditionalTypesSimplifyWhenTrivial.ts, 40, 1)) +>T : Symbol(T, Decl(conditionalTypesSimplifyWhenTrivial.ts, 48, 14)) +>T : Symbol(T, Decl(conditionalTypesSimplifyWhenTrivial.ts, 48, 14)) + + var y: T = x; +>y : Symbol(y, Decl(conditionalTypesSimplifyWhenTrivial.ts, 49, 7)) +>T : Symbol(T, Decl(conditionalTypesSimplifyWhenTrivial.ts, 48, 14)) +>x : Symbol(x, Decl(conditionalTypesSimplifyWhenTrivial.ts, 48, 17)) + + x = y; +>x : Symbol(x, Decl(conditionalTypesSimplifyWhenTrivial.ts, 48, 17)) +>y : Symbol(y, Decl(conditionalTypesSimplifyWhenTrivial.ts, 49, 7)) +} + +const fn11 = ( +>fn11 : Symbol(fn11, Decl(conditionalTypesSimplifyWhenTrivial.ts, 53, 5)) +>Params : Symbol(Params, Decl(conditionalTypesSimplifyWhenTrivial.ts, 53, 14)) + + params: Pick>, +>params : Symbol(params, Decl(conditionalTypesSimplifyWhenTrivial.ts, 53, 22)) +>Pick : Symbol(Pick, Decl(lib.es5.d.ts, --, --)) +>Params : Symbol(Params, Decl(conditionalTypesSimplifyWhenTrivial.ts, 53, 14)) +>TemplatedConditional : Symbol(TemplatedConditional, Decl(conditionalTypesSimplifyWhenTrivial.ts, 40, 1)) +>Params : Symbol(Params, Decl(conditionalTypesSimplifyWhenTrivial.ts, 53, 14)) +>Params : Symbol(Params, Decl(conditionalTypesSimplifyWhenTrivial.ts, 53, 14)) +>Params : Symbol(Params, Decl(conditionalTypesSimplifyWhenTrivial.ts, 53, 14)) + +): Params => params; +>Params : Symbol(Params, Decl(conditionalTypesSimplifyWhenTrivial.ts, 53, 14)) +>params : Symbol(params, Decl(conditionalTypesSimplifyWhenTrivial.ts, 53, 22)) + +function fn12(x: TemplatedConditional) { +>fn12 : Symbol(fn12, Decl(conditionalTypesSimplifyWhenTrivial.ts, 55, 20)) +>T : Symbol(T, Decl(conditionalTypesSimplifyWhenTrivial.ts, 57, 14)) +>x : Symbol(x, Decl(conditionalTypesSimplifyWhenTrivial.ts, 57, 17)) +>TemplatedConditional : Symbol(TemplatedConditional, Decl(conditionalTypesSimplifyWhenTrivial.ts, 40, 1)) +>T : Symbol(T, Decl(conditionalTypesSimplifyWhenTrivial.ts, 57, 14)) +>T : Symbol(T, Decl(conditionalTypesSimplifyWhenTrivial.ts, 57, 14)) +>T : Symbol(T, Decl(conditionalTypesSimplifyWhenTrivial.ts, 57, 14)) + + var y: T = x; +>y : Symbol(y, Decl(conditionalTypesSimplifyWhenTrivial.ts, 58, 7)) +>T : Symbol(T, Decl(conditionalTypesSimplifyWhenTrivial.ts, 57, 14)) +>x : Symbol(x, Decl(conditionalTypesSimplifyWhenTrivial.ts, 57, 17)) + + x = y; +>x : Symbol(x, Decl(conditionalTypesSimplifyWhenTrivial.ts, 57, 17)) +>y : Symbol(y, Decl(conditionalTypesSimplifyWhenTrivial.ts, 58, 7)) +} + diff --git a/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.types b/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.types index 27ca13da91eba..b811b1c0184ac 100644 --- a/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.types +++ b/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.types @@ -103,3 +103,55 @@ function fn8(x: ExtractWithDefault) { >x : T >y : T } + +type TemplatedConditional = TCheck extends TExtends ? TTrue : TFalse; +>TemplatedConditional : TemplatedConditional + +const fn9 = ( +>fn9 : (params: Pick) => Params +>( params: Pick>,): Params => params : (params: Pick) => Params + + params: Pick>, +>params : Pick + +): Params => params; +>params : Pick + +function fn10(x: TemplatedConditional) { +>fn10 : (x: T) => void +>x : T + + var y: T = x; +>y : T +>x : T + + x = y; +>x = y : T +>x : T +>y : T +} + +const fn11 = ( +>fn11 : (params: Pick) => Params +>( params: Pick>,): Params => params : (params: Pick) => Params + + params: Pick>, +>params : Pick + +): Params => params; +>params : Pick + +function fn12(x: TemplatedConditional) { +>fn12 : (x: T) => void +>x : T + + var y: T = x; +>y : T +>x : T + + x = y; +>x = y : T +>x : T +>y : T +} + diff --git a/tests/cases/compiler/conditionalTypesSimplifyWhenTrivial.ts b/tests/cases/compiler/conditionalTypesSimplifyWhenTrivial.ts index 34d3f215d1424..0f2dd342b509f 100644 --- a/tests/cases/compiler/conditionalTypesSimplifyWhenTrivial.ts +++ b/tests/cases/compiler/conditionalTypesSimplifyWhenTrivial.ts @@ -39,4 +39,24 @@ const fn7 = ( function fn8(x: ExtractWithDefault) { var y: T = x; x = y; -} \ No newline at end of file +} + +type TemplatedConditional = TCheck extends TExtends ? TTrue : TFalse; + +const fn9 = ( + params: Pick>, +): Params => params; + +function fn10(x: TemplatedConditional) { + var y: T = x; + x = y; +} + +const fn11 = ( + params: Pick>, +): Params => params; + +function fn12(x: TemplatedConditional) { + var y: T = x; + x = y; +} From 983dd777037577ef66aee0e75970194ebe4f794d Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 23 Jan 2019 10:50:15 -0800 Subject: [PATCH 06/13] Globally cache conditional type instantiations ala indexed access types --- src/compiler/checker.ts | 45 ++++++++++++++----- .../baselines/reference/conditionalTypes1.js | 2 +- .../reference/conditionalTypes1.types | 6 +-- 3 files changed, 37 insertions(+), 16 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e093338812a61..16ac18ba0f84c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -378,6 +378,7 @@ namespace ts { const intersectionTypes = createMap(); const literalTypes = createMap(); const indexedAccessTypes = createMap(); + const conditionalTypes = createMap(); const evolvingArrayTypes: EvolvingArrayType[] = []; const undefinedProperties = createMap() as UnderscoreEscapedMap; @@ -9989,22 +9990,31 @@ namespace ts { // Simplifications for types of the form `T extends U ? T : never` and `T extends U ? never : T`. const trueType = instantiateType(root.trueType, mapper); const falseType = instantiateType(root.falseType, mapper); + const instantiationId = `${root.isDistributive ? "d" : ""}${getTypeId(checkType)}>${getTypeId(extendsType)}?${getTypeId(trueType)}:${getTypeId(falseType)}`; + let result = conditionalTypes.get(instantiationId); + if (result) { + return result; + } if (falseType.flags & TypeFlags.Never && isTypeIdenticalTo(getActualTypeVariable(trueType), getActualTypeVariable(checkType))) { if (isTypeAssignableTo(getRestrictiveInstantiation(checkType), getRestrictiveInstantiation(extendsType))) { // Always true - return getDefaultConstraintOfTrueBranchOfConditionalType(root, /*combinedMapper*/ undefined, mapper); + result = getDefaultConstraintOfTrueBranchOfConditionalType(root, /*combinedMapper*/ undefined, mapper); } else if (getUnionType([getIntersectionType([checkType, extendsType]), neverType]).flags & TypeFlags.Never) { // Always false - return neverType; + result = neverType; } } else if (trueType.flags & TypeFlags.Never && isTypeIdenticalTo(getActualTypeVariable(falseType), getActualTypeVariable(checkType))) { if (isTypeAssignableTo(getRestrictiveInstantiation(checkType), getRestrictiveInstantiation(extendsType))) { // Always true - return neverType; + result = neverType; } else if (getUnionType([getIntersectionType([checkType, extendsType]), neverType]).flags & TypeFlags.Never) { // Always false - return falseType; // TODO: Intersect negated `extends` type here + result = falseType; // TODO: Intersect negated `extends` type here } } + if (result) { + conditionalTypes.set(instantiationId, result); + return result; + } const checkTypeInstantiable = maybeTypeOfKind(checkType, TypeFlags.Instantiable | TypeFlags.GenericMappedType); let combinedMapper: TypeMapper | undefined; if (root.inferTypeParameters) { @@ -10022,17 +10032,21 @@ namespace ts { // We attempt to resolve the conditional type only when the check and extends types are non-generic if (!checkTypeInstantiable && !maybeTypeOfKind(inferredExtendsType, TypeFlags.Instantiable | TypeFlags.GenericMappedType)) { if (inferredExtendsType.flags & TypeFlags.AnyOrUnknown) { + conditionalTypes.set(instantiationId, trueType); return trueType; } // Return union of trueType and falseType for 'any' since it matches anything if (checkType.flags & TypeFlags.Any) { - return getUnionType([instantiateType(root.trueType, combinedMapper || mapper), falseType]); + result = getUnionType([instantiateType(root.trueType, combinedMapper || mapper), falseType]); + conditionalTypes.set(instantiationId, result); + return result; } // Return falseType for a definitely false extends check. We check an instantations of the two // types with type parameters mapped to the wildcard type, the most permissive instantiations // possible (the wildcard type is assignable to and from all types). If those are not related, // then no instatiations will be and we can just return the false branch type. if (!isTypeAssignableTo(getPermissiveInstantiation(checkType), getPermissiveInstantiation(inferredExtendsType))) { + conditionalTypes.set(instantiationId, falseType); return falseType; } // Return trueType for a definitely true extends check. We check instantiations of the two @@ -10041,19 +10055,26 @@ namespace ts { // type Foo = T extends { x: string } ? string : number // doesn't immediately resolve to 'string' instead of being deferred. if (isTypeAssignableTo(getRestrictiveInstantiation(checkType), getRestrictiveInstantiation(inferredExtendsType))) { - return instantiateType(root.trueType, combinedMapper || mapper); + result = instantiateType(root.trueType, combinedMapper || mapper); + conditionalTypes.set(instantiationId, result); + return result; } } // Return a deferred type for a check that is neither definitely true nor definitely false const erasedCheckType = getActualTypeVariable(checkType); - const result = createType(TypeFlags.Conditional); - result.root = root; - result.checkType = erasedCheckType; - result.extendsType = extendsType; - result.mapper = mapper; - result.combinedMapper = combinedMapper; + result = createType(TypeFlags.Conditional); + (result as ConditionalType).root = root; + (result as ConditionalType).checkType = erasedCheckType; + (result as ConditionalType).extendsType = extendsType; + (result as ConditionalType).mapper = mapper; + (result as ConditionalType).combinedMapper = combinedMapper; + if (!combinedMapper) { + (result as ConditionalType).resolvedTrueType = trueType; + (result as ConditionalType).resolvedFalseType = falseType; + } result.aliasSymbol = root.aliasSymbol; result.aliasTypeArguments = instantiateTypes(root.aliasTypeArguments, mapper!); // TODO: GH#18217 + conditionalTypes.set(instantiationId, result); return result; } diff --git a/tests/baselines/reference/conditionalTypes1.js b/tests/baselines/reference/conditionalTypes1.js index 34530db9fa309..b592bd33d10e8 100644 --- a/tests/baselines/reference/conditionalTypes1.js +++ b/tests/baselines/reference/conditionalTypes1.js @@ -647,7 +647,7 @@ declare type T82 = Eq2; declare type T83 = Eq2; declare type Foo = T extends string ? boolean : number; declare type Bar = T extends string ? boolean : number; -declare const convert: (value: Foo) => Bar; +declare const convert: (value: Foo) => Foo; declare type Baz = Foo; declare const convert2: (value: Foo) => Foo; declare function f31(): void; diff --git a/tests/baselines/reference/conditionalTypes1.types b/tests/baselines/reference/conditionalTypes1.types index 0ff18e27f25fd..8cca94f0a1021 100644 --- a/tests/baselines/reference/conditionalTypes1.types +++ b/tests/baselines/reference/conditionalTypes1.types @@ -779,8 +779,8 @@ type Bar = T extends string ? boolean : number; >Bar : Bar const convert = (value: Foo): Bar => value; ->convert : (value: Foo) => Bar ->(value: Foo): Bar => value : (value: Foo) => Bar +>convert : (value: Foo) => Foo +>(value: Foo): Bar => value : (value: Foo) => Foo >value : Foo >value : Foo @@ -832,7 +832,7 @@ function f33() { >T1 : Foo type T2 = Bar; ->T2 : Bar +>T2 : Foo var z: T1; >z : Foo From c159aaa3f2f1597828ef6b004a0374adaff3f3d4 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 23 Jan 2019 11:19:06 -0800 Subject: [PATCH 07/13] Handle `any` simplifications --- src/compiler/checker.ts | 6 +++--- .../reference/conditionalTypesSimplifyWhenTrivial.js | 4 ++++ .../conditionalTypesSimplifyWhenTrivial.symbols | 7 +++++++ .../conditionalTypesSimplifyWhenTrivial.types | 10 ++++++++++ .../compiler/conditionalTypesSimplifyWhenTrivial.ts | 3 +++ 5 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 16ac18ba0f84c..fabfa1f49c906 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -9996,7 +9996,7 @@ namespace ts { return result; } if (falseType.flags & TypeFlags.Never && isTypeIdenticalTo(getActualTypeVariable(trueType), getActualTypeVariable(checkType))) { - if (isTypeAssignableTo(getRestrictiveInstantiation(checkType), getRestrictiveInstantiation(extendsType))) { // Always true + if (checkType.flags & TypeFlags.Any || isTypeAssignableTo(getRestrictiveInstantiation(checkType), getRestrictiveInstantiation(extendsType))) { // Always true result = getDefaultConstraintOfTrueBranchOfConditionalType(root, /*combinedMapper*/ undefined, mapper); } else if (getUnionType([getIntersectionType([checkType, extendsType]), neverType]).flags & TypeFlags.Never) { // Always false @@ -10004,10 +10004,10 @@ namespace ts { } } else if (trueType.flags & TypeFlags.Never && isTypeIdenticalTo(getActualTypeVariable(falseType), getActualTypeVariable(checkType))) { - if (isTypeAssignableTo(getRestrictiveInstantiation(checkType), getRestrictiveInstantiation(extendsType))) { // Always true + if (!(checkType.flags & TypeFlags.Any) && isTypeAssignableTo(getRestrictiveInstantiation(checkType), getRestrictiveInstantiation(extendsType))) { // Always true result = neverType; } - else if (getUnionType([getIntersectionType([checkType, extendsType]), neverType]).flags & TypeFlags.Never) { // Always false + else if (checkType.flags & TypeFlags.Any || getUnionType([getIntersectionType([checkType, extendsType]), neverType]).flags & TypeFlags.Never) { // Always false result = falseType; // TODO: Intersect negated `extends` type here } } diff --git a/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.js b/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.js index 84b243330d7e5..41b9476db8828 100644 --- a/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.js +++ b/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.js @@ -60,6 +60,9 @@ function fn12(x: TemplatedConditional) { var y: T = x; x = y; } + +declare var z: any; +const zee = z!!!; // since x is `any`, `x extends null | undefined` should be both true and false - and thus yield `any` //// [conditionalTypesSimplifyWhenTrivial.js] @@ -94,3 +97,4 @@ function fn12(x) { var y = x; x = y; } +var zee = z; // since x is `any`, `x extends null | undefined` should be both true and false - and thus yield `any` diff --git a/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.symbols b/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.symbols index 248a91fd03a71..f8b99f9eb6ae2 100644 --- a/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.symbols +++ b/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.symbols @@ -236,3 +236,10 @@ function fn12(x: TemplatedConditional) { >y : Symbol(y, Decl(conditionalTypesSimplifyWhenTrivial.ts, 58, 7)) } +declare var z: any; +>z : Symbol(z, Decl(conditionalTypesSimplifyWhenTrivial.ts, 62, 11)) + +const zee = z!!!; // since x is `any`, `x extends null | undefined` should be both true and false - and thus yield `any` +>zee : Symbol(zee, Decl(conditionalTypesSimplifyWhenTrivial.ts, 63, 5)) +>z : Symbol(z, Decl(conditionalTypesSimplifyWhenTrivial.ts, 62, 11)) + diff --git a/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.types b/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.types index b811b1c0184ac..5f5131535605e 100644 --- a/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.types +++ b/tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.types @@ -155,3 +155,13 @@ function fn12(x: TemplatedConditional) { >y : T } +declare var z: any; +>z : any + +const zee = z!!!; // since x is `any`, `x extends null | undefined` should be both true and false - and thus yield `any` +>zee : any +>z!!! : any +>z!! : any +>z! : any +>z : any + diff --git a/tests/cases/compiler/conditionalTypesSimplifyWhenTrivial.ts b/tests/cases/compiler/conditionalTypesSimplifyWhenTrivial.ts index 0f2dd342b509f..de05b820de33b 100644 --- a/tests/cases/compiler/conditionalTypesSimplifyWhenTrivial.ts +++ b/tests/cases/compiler/conditionalTypesSimplifyWhenTrivial.ts @@ -60,3 +60,6 @@ function fn12(x: TemplatedConditional) { var y: T = x; x = y; } + +declare var z: any; +const zee = z!!!; // since x is `any`, `x extends null | undefined` should be both true and false - and thus yield `any` From d60667e4e8a19c1365bf0d045ba7e45924eba55a Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 20 Feb 2019 10:18:38 -0800 Subject: [PATCH 08/13] Factor empty intersection check into function --- src/compiler/checker.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3d6b469b92305..2bba20e0ece95 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -10058,6 +10058,13 @@ namespace ts { return type.flags & TypeFlags.Substitution ? (type).typeVariable : type; } + /** + * Invokes union simplification logic to determine if an intersection is considered empty as a union constituent + */ + function isIntersectionEmpty(type1: Type, type2: Type) { + return !!(getUnionType([intersectTypes(type1, type2), neverType]).flags & TypeFlags.Never); + } + function getConditionalType(root: ConditionalRoot, mapper: TypeMapper | undefined): Type { const checkType = instantiateType(root.checkType, mapper); const extendsType = instantiateType(root.extendsType, mapper); @@ -10076,7 +10083,7 @@ namespace ts { if (checkType.flags & TypeFlags.Any || isTypeAssignableTo(getRestrictiveInstantiation(checkType), getRestrictiveInstantiation(extendsType))) { // Always true result = getDefaultConstraintOfTrueBranchOfConditionalType(root, /*combinedMapper*/ undefined, mapper); } - else if (getUnionType([getIntersectionType([checkType, extendsType]), neverType]).flags & TypeFlags.Never) { // Always false + else if (isIntersectionEmpty(checkType, extendsType)) { // Always false result = neverType; } } @@ -10084,7 +10091,7 @@ namespace ts { if (!(checkType.flags & TypeFlags.Any) && isTypeAssignableTo(getRestrictiveInstantiation(checkType), getRestrictiveInstantiation(extendsType))) { // Always true result = neverType; } - else if (checkType.flags & TypeFlags.Any || getUnionType([getIntersectionType([checkType, extendsType]), neverType]).flags & TypeFlags.Never) { // Always false + else if (checkType.flags & TypeFlags.Any || isIntersectionEmpty(checkType, extendsType)) { // Always false result = falseType; // TODO: Intersect negated `extends` type here } } From 917b904a1f4a634bd32724e613195cdf7daa76f6 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 7 Mar 2019 14:53:24 -0800 Subject: [PATCH 09/13] Modifify conditional type constraints to better handle single-branch `any` and restrictive type parameters --- src/compiler/checker.ts | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f7f795d7897ee..d7e512d865176 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7472,7 +7472,13 @@ namespace ts { function getDefaultConstraintOfConditionalType(type: ConditionalType) { if (!type.resolvedDefaultConstraint) { - type.resolvedDefaultConstraint = getUnionType([getDefaultConstraintOfTrueBranchOfConditionalType(type.root, type.combinedMapper, type.mapper), getFalseTypeFromConditionalType(type)]); + // An `any` branch of a conditional type would normally be viral - specifically, without special handling here, + // a conditional type with a single branch of type `any` would be assignable to anything, since it's constraint would simplify to + // just `any`. This result is _usually_ unwanted - so instead here we elide an `any` branch from the constraint type, + // in effect treating `any` like `never` rather than `unknown` in this location. + const trueConstraint = getDefaultConstraintOfTrueBranchOfConditionalType(type.root, type.combinedMapper, type.mapper); + const falseConstraint = getFalseTypeFromConditionalType(type); + type.resolvedDefaultConstraint = isTypeAny(trueConstraint) ? falseConstraint : isTypeAny(falseConstraint) ? trueConstraint : getUnionType([trueConstraint, falseConstraint]); } return type.resolvedDefaultConstraint; } @@ -7483,7 +7489,13 @@ namespace ts { // with its constraint. We do this because if the constraint is a union type it will be distributed // over the conditional type and possibly reduced. For example, 'T extends undefined ? never : T' // removes 'undefined' from T. - if (type.root.isDistributive) { + // We skip returning a distributive constraint for a restrictive instantiation of a conditional type + // as the constraint for all type params (check type included) have been replace with `unknown`, which + // is going to produce even more false positive/negative results than the distribute constraint already does. + // Please note: the distributive constraint is a kludge for emulating what a negated type could to do filter + // a union - once negated types exist and are applied to the conditional false branch, this "constraint" + // likely doesn't need to exist. + if (type.root.isDistributive && type.restrictiveInstantiation !== type) { const simplified = getSimplifiedType(type.checkType); const constraint = simplified === type.checkType ? getConstraintOfType(simplified) : simplified; if (constraint && constraint !== type.checkType) { @@ -11166,8 +11178,20 @@ namespace ts { } function getRestrictiveInstantiation(type: Type) { - return type.flags & (TypeFlags.Primitive | TypeFlags.AnyOrUnknown | TypeFlags.Never) ? type : - type.restrictiveInstantiation || (type.restrictiveInstantiation = instantiateType(type, restrictiveMapper)); + if (type.flags & (TypeFlags.Primitive | TypeFlags.AnyOrUnknown | TypeFlags.Never)) { + return type; + } + if (type.restrictiveInstantiation) { + return type.restrictiveInstantiation; + } + type.restrictiveInstantiation = instantiateType(type, restrictiveMapper); + // We set the following so we don't attempt to set the restrictive instance of a restrictive instance + // which is redundant - we'll produce new type identities, but all type params have already been mapped. + // This also gives us a way to detect restrictive instances upon comparisons and _disable_ the "distributeive constraint" + // assignability check for them, which is distinctly unsafe, as once you have a restrctive instance, all the type parameters + // are constrained to `unknown` and produce tons of false positives/negatives! + type.restrictiveInstantiation.restrictiveInstantiation = type.restrictiveInstantiation; + return type.restrictiveInstantiation; } function instantiateIndexInfo(info: IndexInfo | undefined, mapper: TypeMapper): IndexInfo | undefined { From dd36e01baaab2514b5abe842737bae20b11bc624 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 7 Mar 2019 14:57:28 -0800 Subject: [PATCH 10/13] Add test case motivating prior commit --- .../reference/propTypeValidatorInference.js | 121 ++++++ .../propTypeValidatorInference.symbols | 366 ++++++++++++++++++ .../propTypeValidatorInference.types | 301 ++++++++++++++ .../compiler/propTypeValidatorInference.ts | 89 +++++ 4 files changed, 877 insertions(+) create mode 100644 tests/baselines/reference/propTypeValidatorInference.js create mode 100644 tests/baselines/reference/propTypeValidatorInference.symbols create mode 100644 tests/baselines/reference/propTypeValidatorInference.types create mode 100644 tests/cases/compiler/propTypeValidatorInference.ts diff --git a/tests/baselines/reference/propTypeValidatorInference.js b/tests/baselines/reference/propTypeValidatorInference.js new file mode 100644 index 0000000000000..3a6fa04fbe183 --- /dev/null +++ b/tests/baselines/reference/propTypeValidatorInference.js @@ -0,0 +1,121 @@ +//// [tests/cases/compiler/propTypeValidatorInference.ts] //// + +//// [index.d.ts] +export const nominalTypeHack: unique symbol; + +export type IsOptional = undefined | null extends T ? true : undefined extends T ? true : null extends T ? true : false; + +export type RequiredKeys = { [K in keyof V]-?: Exclude extends Validator ? IsOptional extends true ? never : K : never }[keyof V]; +export type OptionalKeys = Exclude>; +export type InferPropsInner = { [K in keyof V]-?: InferType; }; + +export interface Validator { + (props: object, propName: string, componentName: string, location: string, propFullName: string): Error | null; + [nominalTypeHack]?: T; +} + +export interface Requireable extends Validator { + isRequired: Validator>; +} + +export type ValidationMap = { [K in keyof T]?: Validator }; + +export type InferType = V extends Validator ? T : any; +export type InferProps = + & InferPropsInner>> + & Partial>>>; + +export const any: Requireable; +export const array: Requireable; +export const bool: Requireable; +export const string: Requireable; +export const number: Requireable; +export function shape

>(type: P): Requireable>; +export function oneOfType>(types: T[]): Requireable>>; + + +//// [file.ts] +import * as PropTypes from "prop-types"; +interface Props { + any?: any; + array: string[]; + bool: boolean; + shape: { + foo: string; + bar?: boolean; + baz?: any + }; + oneOfType: string | boolean | { + foo?: string; + bar: number; + }; +} + +type PropTypesMap = PropTypes.ValidationMap; + +const innerProps = { + foo: PropTypes.string.isRequired, + bar: PropTypes.bool, + baz: PropTypes.any +}; + +const arrayOfTypes = [PropTypes.string, PropTypes.bool, PropTypes.shape({ + foo: PropTypes.string, + bar: PropTypes.number.isRequired +})]; + +// TS checking +const propTypes: PropTypesMap = { + any: PropTypes.any, + array: PropTypes.array.isRequired, + bool: PropTypes.bool.isRequired, + shape: PropTypes.shape(innerProps).isRequired, + oneOfType: PropTypes.oneOfType(arrayOfTypes).isRequired, +}; + +// JS checking +const propTypesWithoutAnnotation = { + any: PropTypes.any, + array: PropTypes.array.isRequired, + bool: PropTypes.bool.isRequired, + shape: PropTypes.shape(innerProps).isRequired, + oneOfType: PropTypes.oneOfType(arrayOfTypes).isRequired, +}; + +type ExtractedProps = PropTypes.InferProps; + +type ExtractedPropsWithoutAnnotation = PropTypes.InferProps; + +type ExtractPropsMatch = ExtractedProps extends ExtractedPropsWithoutAnnotation ? true : false; +const x: true = (null as any as ExtractPropsMatch); + +//// [file.js] +"use strict"; +exports.__esModule = true; +var PropTypes = require("prop-types"); +var innerProps = { + foo: PropTypes.string.isRequired, + bar: PropTypes.bool, + baz: PropTypes.any +}; +var arrayOfTypes = [PropTypes.string, PropTypes.bool, PropTypes.shape({ + foo: PropTypes.string, + bar: PropTypes.number.isRequired + })]; +// TS checking +var propTypes = { + any: PropTypes.any, + array: PropTypes.array.isRequired, + bool: PropTypes.bool.isRequired, + shape: PropTypes.shape(innerProps).isRequired, + oneOfType: PropTypes.oneOfType(arrayOfTypes).isRequired +}; +// JS checking +var propTypesWithoutAnnotation = { + any: PropTypes.any, + array: PropTypes.array.isRequired, + bool: PropTypes.bool.isRequired, + shape: PropTypes.shape(innerProps).isRequired, + oneOfType: PropTypes.oneOfType(arrayOfTypes).isRequired +}; +var x = null; diff --git a/tests/baselines/reference/propTypeValidatorInference.symbols b/tests/baselines/reference/propTypeValidatorInference.symbols new file mode 100644 index 0000000000000..32a4a3c1abf3b --- /dev/null +++ b/tests/baselines/reference/propTypeValidatorInference.symbols @@ -0,0 +1,366 @@ +=== tests/cases/compiler/node_modules/prop-types/index.d.ts === +export const nominalTypeHack: unique symbol; +>nominalTypeHack : Symbol(nominalTypeHack, Decl(index.d.ts, 0, 12)) + +export type IsOptional = undefined | null extends T ? true : undefined extends T ? true : null extends T ? true : false; +>IsOptional : Symbol(IsOptional, Decl(index.d.ts, 0, 44)) +>T : Symbol(T, Decl(index.d.ts, 2, 23)) +>T : Symbol(T, Decl(index.d.ts, 2, 23)) +>T : Symbol(T, Decl(index.d.ts, 2, 23)) +>T : Symbol(T, Decl(index.d.ts, 2, 23)) + +export type RequiredKeys = { [K in keyof V]-?: Exclude extends Validator ? IsOptional extends true ? never : K : never }[keyof V]; +>RequiredKeys : Symbol(RequiredKeys, Decl(index.d.ts, 2, 123)) +>V : Symbol(V, Decl(index.d.ts, 4, 25)) +>K : Symbol(K, Decl(index.d.ts, 4, 33)) +>V : Symbol(V, Decl(index.d.ts, 4, 25)) +>Exclude : Symbol(Exclude, Decl(lib.es5.d.ts, --, --)) +>V : Symbol(V, Decl(index.d.ts, 4, 25)) +>K : Symbol(K, Decl(index.d.ts, 4, 33)) +>Validator : Symbol(Validator, Decl(index.d.ts, 6, 72)) +>T : Symbol(T, Decl(index.d.ts, 4, 98)) +>IsOptional : Symbol(IsOptional, Decl(index.d.ts, 0, 44)) +>T : Symbol(T, Decl(index.d.ts, 4, 98)) +>K : Symbol(K, Decl(index.d.ts, 4, 33)) +>V : Symbol(V, Decl(index.d.ts, 4, 25)) + +export type OptionalKeys = Exclude>; +>OptionalKeys : Symbol(OptionalKeys, Decl(index.d.ts, 4, 162)) +>V : Symbol(V, Decl(index.d.ts, 5, 25)) +>Exclude : Symbol(Exclude, Decl(lib.es5.d.ts, --, --)) +>V : Symbol(V, Decl(index.d.ts, 5, 25)) +>RequiredKeys : Symbol(RequiredKeys, Decl(index.d.ts, 2, 123)) +>V : Symbol(V, Decl(index.d.ts, 5, 25)) + +export type InferPropsInner = { [K in keyof V]-?: InferType; }; +>InferPropsInner : Symbol(InferPropsInner, Decl(index.d.ts, 5, 64)) +>V : Symbol(V, Decl(index.d.ts, 6, 28)) +>K : Symbol(K, Decl(index.d.ts, 6, 36)) +>V : Symbol(V, Decl(index.d.ts, 6, 28)) +>InferType : Symbol(InferType, Decl(index.d.ts, 17, 68)) +>V : Symbol(V, Decl(index.d.ts, 6, 28)) +>K : Symbol(K, Decl(index.d.ts, 6, 36)) + +export interface Validator { +>Validator : Symbol(Validator, Decl(index.d.ts, 6, 72)) +>T : Symbol(T, Decl(index.d.ts, 8, 27)) + + (props: object, propName: string, componentName: string, location: string, propFullName: string): Error | null; +>props : Symbol(props, Decl(index.d.ts, 9, 5)) +>propName : Symbol(propName, Decl(index.d.ts, 9, 19)) +>componentName : Symbol(componentName, Decl(index.d.ts, 9, 37)) +>location : Symbol(location, Decl(index.d.ts, 9, 60)) +>propFullName : Symbol(propFullName, Decl(index.d.ts, 9, 78)) +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + [nominalTypeHack]?: T; +>[nominalTypeHack] : Symbol(Validator[nominalTypeHack], Decl(index.d.ts, 9, 115)) +>nominalTypeHack : Symbol(nominalTypeHack, Decl(index.d.ts, 0, 12)) +>T : Symbol(T, Decl(index.d.ts, 8, 27)) +} + +export interface Requireable extends Validator { +>Requireable : Symbol(Requireable, Decl(index.d.ts, 11, 1)) +>T : Symbol(T, Decl(index.d.ts, 13, 29)) +>Validator : Symbol(Validator, Decl(index.d.ts, 6, 72)) +>T : Symbol(T, Decl(index.d.ts, 13, 29)) + + isRequired: Validator>; +>isRequired : Symbol(Requireable.isRequired, Decl(index.d.ts, 13, 73)) +>Validator : Symbol(Validator, Decl(index.d.ts, 6, 72)) +>NonNullable : Symbol(NonNullable, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(index.d.ts, 13, 29)) +} + +export type ValidationMap = { [K in keyof T]?: Validator }; +>ValidationMap : Symbol(ValidationMap, Decl(index.d.ts, 15, 1)) +>T : Symbol(T, Decl(index.d.ts, 17, 26)) +>K : Symbol(K, Decl(index.d.ts, 17, 34)) +>T : Symbol(T, Decl(index.d.ts, 17, 26)) +>Validator : Symbol(Validator, Decl(index.d.ts, 6, 72)) +>T : Symbol(T, Decl(index.d.ts, 17, 26)) +>K : Symbol(K, Decl(index.d.ts, 17, 34)) + +export type InferType = V extends Validator ? T : any; +>InferType : Symbol(InferType, Decl(index.d.ts, 17, 68)) +>V : Symbol(V, Decl(index.d.ts, 19, 22)) +>V : Symbol(V, Decl(index.d.ts, 19, 22)) +>Validator : Symbol(Validator, Decl(index.d.ts, 6, 72)) +>T : Symbol(T, Decl(index.d.ts, 19, 52)) +>T : Symbol(T, Decl(index.d.ts, 19, 52)) + +export type InferProps = +>InferProps : Symbol(InferProps, Decl(index.d.ts, 19, 66)) +>V : Symbol(V, Decl(index.d.ts, 20, 23)) + + & InferPropsInner>> +>InferPropsInner : Symbol(InferPropsInner, Decl(index.d.ts, 5, 64)) +>Pick : Symbol(Pick, Decl(lib.es5.d.ts, --, --)) +>V : Symbol(V, Decl(index.d.ts, 20, 23)) +>RequiredKeys : Symbol(RequiredKeys, Decl(index.d.ts, 2, 123)) +>V : Symbol(V, Decl(index.d.ts, 20, 23)) + + & Partial>>>; +>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --)) +>InferPropsInner : Symbol(InferPropsInner, Decl(index.d.ts, 5, 64)) +>Pick : Symbol(Pick, Decl(lib.es5.d.ts, --, --)) +>V : Symbol(V, Decl(index.d.ts, 20, 23)) +>OptionalKeys : Symbol(OptionalKeys, Decl(index.d.ts, 4, 162)) +>V : Symbol(V, Decl(index.d.ts, 20, 23)) + +export const any: Requireable; +>any : Symbol(any, Decl(index.d.ts, 24, 12)) +>Requireable : Symbol(Requireable, Decl(index.d.ts, 11, 1)) + +export const array: Requireable; +>array : Symbol(array, Decl(index.d.ts, 25, 12)) +>Requireable : Symbol(Requireable, Decl(index.d.ts, 11, 1)) + +export const bool: Requireable; +>bool : Symbol(bool, Decl(index.d.ts, 26, 12)) +>Requireable : Symbol(Requireable, Decl(index.d.ts, 11, 1)) + +export const string: Requireable; +>string : Symbol(string, Decl(index.d.ts, 27, 12)) +>Requireable : Symbol(Requireable, Decl(index.d.ts, 11, 1)) + +export const number: Requireable; +>number : Symbol(number, Decl(index.d.ts, 28, 12)) +>Requireable : Symbol(Requireable, Decl(index.d.ts, 11, 1)) + +export function shape

>(type: P): Requireable>; +>shape : Symbol(shape, Decl(index.d.ts, 28, 41)) +>P : Symbol(P, Decl(index.d.ts, 29, 22)) +>ValidationMap : Symbol(ValidationMap, Decl(index.d.ts, 15, 1)) +>type : Symbol(type, Decl(index.d.ts, 29, 52)) +>P : Symbol(P, Decl(index.d.ts, 29, 22)) +>Requireable : Symbol(Requireable, Decl(index.d.ts, 11, 1)) +>InferProps : Symbol(InferProps, Decl(index.d.ts, 19, 66)) +>P : Symbol(P, Decl(index.d.ts, 29, 22)) + +export function oneOfType>(types: T[]): Requireable>>; +>oneOfType : Symbol(oneOfType, Decl(index.d.ts, 29, 89)) +>T : Symbol(T, Decl(index.d.ts, 30, 26)) +>Validator : Symbol(Validator, Decl(index.d.ts, 6, 72)) +>types : Symbol(types, Decl(index.d.ts, 30, 52)) +>T : Symbol(T, Decl(index.d.ts, 30, 26)) +>Requireable : Symbol(Requireable, Decl(index.d.ts, 11, 1)) +>NonNullable : Symbol(NonNullable, Decl(lib.es5.d.ts, --, --)) +>InferType : Symbol(InferType, Decl(index.d.ts, 17, 68)) +>T : Symbol(T, Decl(index.d.ts, 30, 26)) + + +=== tests/cases/compiler/file.ts === +import * as PropTypes from "prop-types"; +>PropTypes : Symbol(PropTypes, Decl(file.ts, 0, 6)) + +interface Props { +>Props : Symbol(Props, Decl(file.ts, 0, 40)) + + any?: any; +>any : Symbol(Props.any, Decl(file.ts, 1, 17)) + + array: string[]; +>array : Symbol(Props.array, Decl(file.ts, 2, 14)) + + bool: boolean; +>bool : Symbol(Props.bool, Decl(file.ts, 3, 20)) + + shape: { +>shape : Symbol(Props.shape, Decl(file.ts, 4, 18)) + + foo: string; +>foo : Symbol(foo, Decl(file.ts, 5, 12)) + + bar?: boolean; +>bar : Symbol(bar, Decl(file.ts, 6, 20)) + + baz?: any +>baz : Symbol(baz, Decl(file.ts, 7, 22)) + + }; + oneOfType: string | boolean | { +>oneOfType : Symbol(Props.oneOfType, Decl(file.ts, 9, 6)) + + foo?: string; +>foo : Symbol(foo, Decl(file.ts, 10, 35)) + + bar: number; +>bar : Symbol(bar, Decl(file.ts, 11, 21)) + + }; +} + +type PropTypesMap = PropTypes.ValidationMap; +>PropTypesMap : Symbol(PropTypesMap, Decl(file.ts, 14, 1)) +>PropTypes : Symbol(PropTypes, Decl(file.ts, 0, 6)) +>ValidationMap : Symbol(PropTypes.ValidationMap, Decl(index.d.ts, 15, 1)) +>Props : Symbol(Props, Decl(file.ts, 0, 40)) + +const innerProps = { +>innerProps : Symbol(innerProps, Decl(file.ts, 18, 5)) + + foo: PropTypes.string.isRequired, +>foo : Symbol(foo, Decl(file.ts, 18, 20)) +>PropTypes.string.isRequired : Symbol(PropTypes.Requireable.isRequired, Decl(index.d.ts, 13, 73)) +>PropTypes.string : Symbol(PropTypes.string, Decl(index.d.ts, 27, 12)) +>PropTypes : Symbol(PropTypes, Decl(file.ts, 0, 6)) +>string : Symbol(PropTypes.string, Decl(index.d.ts, 27, 12)) +>isRequired : Symbol(PropTypes.Requireable.isRequired, Decl(index.d.ts, 13, 73)) + + bar: PropTypes.bool, +>bar : Symbol(bar, Decl(file.ts, 19, 37)) +>PropTypes.bool : Symbol(PropTypes.bool, Decl(index.d.ts, 26, 12)) +>PropTypes : Symbol(PropTypes, Decl(file.ts, 0, 6)) +>bool : Symbol(PropTypes.bool, Decl(index.d.ts, 26, 12)) + + baz: PropTypes.any +>baz : Symbol(baz, Decl(file.ts, 20, 24)) +>PropTypes.any : Symbol(PropTypes.any, Decl(index.d.ts, 24, 12)) +>PropTypes : Symbol(PropTypes, Decl(file.ts, 0, 6)) +>any : Symbol(PropTypes.any, Decl(index.d.ts, 24, 12)) + +}; + +const arrayOfTypes = [PropTypes.string, PropTypes.bool, PropTypes.shape({ +>arrayOfTypes : Symbol(arrayOfTypes, Decl(file.ts, 24, 5)) +>PropTypes.string : Symbol(PropTypes.string, Decl(index.d.ts, 27, 12)) +>PropTypes : Symbol(PropTypes, Decl(file.ts, 0, 6)) +>string : Symbol(PropTypes.string, Decl(index.d.ts, 27, 12)) +>PropTypes.bool : Symbol(PropTypes.bool, Decl(index.d.ts, 26, 12)) +>PropTypes : Symbol(PropTypes, Decl(file.ts, 0, 6)) +>bool : Symbol(PropTypes.bool, Decl(index.d.ts, 26, 12)) +>PropTypes.shape : Symbol(PropTypes.shape, Decl(index.d.ts, 28, 41)) +>PropTypes : Symbol(PropTypes, Decl(file.ts, 0, 6)) +>shape : Symbol(PropTypes.shape, Decl(index.d.ts, 28, 41)) + + foo: PropTypes.string, +>foo : Symbol(foo, Decl(file.ts, 24, 73)) +>PropTypes.string : Symbol(PropTypes.string, Decl(index.d.ts, 27, 12)) +>PropTypes : Symbol(PropTypes, Decl(file.ts, 0, 6)) +>string : Symbol(PropTypes.string, Decl(index.d.ts, 27, 12)) + + bar: PropTypes.number.isRequired +>bar : Symbol(bar, Decl(file.ts, 25, 26)) +>PropTypes.number.isRequired : Symbol(PropTypes.Requireable.isRequired, Decl(index.d.ts, 13, 73)) +>PropTypes.number : Symbol(PropTypes.number, Decl(index.d.ts, 28, 12)) +>PropTypes : Symbol(PropTypes, Decl(file.ts, 0, 6)) +>number : Symbol(PropTypes.number, Decl(index.d.ts, 28, 12)) +>isRequired : Symbol(PropTypes.Requireable.isRequired, Decl(index.d.ts, 13, 73)) + +})]; + +// TS checking +const propTypes: PropTypesMap = { +>propTypes : Symbol(propTypes, Decl(file.ts, 30, 5)) +>PropTypesMap : Symbol(PropTypesMap, Decl(file.ts, 14, 1)) + + any: PropTypes.any, +>any : Symbol(any, Decl(file.ts, 30, 33)) +>PropTypes.any : Symbol(PropTypes.any, Decl(index.d.ts, 24, 12)) +>PropTypes : Symbol(PropTypes, Decl(file.ts, 0, 6)) +>any : Symbol(PropTypes.any, Decl(index.d.ts, 24, 12)) + + array: PropTypes.array.isRequired, +>array : Symbol(array, Decl(file.ts, 31, 23)) +>PropTypes.array.isRequired : Symbol(PropTypes.Requireable.isRequired, Decl(index.d.ts, 13, 73)) +>PropTypes.array : Symbol(PropTypes.array, Decl(index.d.ts, 25, 12)) +>PropTypes : Symbol(PropTypes, Decl(file.ts, 0, 6)) +>array : Symbol(PropTypes.array, Decl(index.d.ts, 25, 12)) +>isRequired : Symbol(PropTypes.Requireable.isRequired, Decl(index.d.ts, 13, 73)) + + bool: PropTypes.bool.isRequired, +>bool : Symbol(bool, Decl(file.ts, 32, 38)) +>PropTypes.bool.isRequired : Symbol(PropTypes.Requireable.isRequired, Decl(index.d.ts, 13, 73)) +>PropTypes.bool : Symbol(PropTypes.bool, Decl(index.d.ts, 26, 12)) +>PropTypes : Symbol(PropTypes, Decl(file.ts, 0, 6)) +>bool : Symbol(PropTypes.bool, Decl(index.d.ts, 26, 12)) +>isRequired : Symbol(PropTypes.Requireable.isRequired, Decl(index.d.ts, 13, 73)) + + shape: PropTypes.shape(innerProps).isRequired, +>shape : Symbol(shape, Decl(file.ts, 33, 36)) +>PropTypes.shape(innerProps).isRequired : Symbol(PropTypes.Requireable.isRequired, Decl(index.d.ts, 13, 73)) +>PropTypes.shape : Symbol(PropTypes.shape, Decl(index.d.ts, 28, 41)) +>PropTypes : Symbol(PropTypes, Decl(file.ts, 0, 6)) +>shape : Symbol(PropTypes.shape, Decl(index.d.ts, 28, 41)) +>innerProps : Symbol(innerProps, Decl(file.ts, 18, 5)) +>isRequired : Symbol(PropTypes.Requireable.isRequired, Decl(index.d.ts, 13, 73)) + + oneOfType: PropTypes.oneOfType(arrayOfTypes).isRequired, +>oneOfType : Symbol(oneOfType, Decl(file.ts, 34, 50)) +>PropTypes.oneOfType(arrayOfTypes).isRequired : Symbol(PropTypes.Requireable.isRequired, Decl(index.d.ts, 13, 73)) +>PropTypes.oneOfType : Symbol(PropTypes.oneOfType, Decl(index.d.ts, 29, 89)) +>PropTypes : Symbol(PropTypes, Decl(file.ts, 0, 6)) +>oneOfType : Symbol(PropTypes.oneOfType, Decl(index.d.ts, 29, 89)) +>arrayOfTypes : Symbol(arrayOfTypes, Decl(file.ts, 24, 5)) +>isRequired : Symbol(PropTypes.Requireable.isRequired, Decl(index.d.ts, 13, 73)) + +}; + +// JS checking +const propTypesWithoutAnnotation = { +>propTypesWithoutAnnotation : Symbol(propTypesWithoutAnnotation, Decl(file.ts, 39, 5)) + + any: PropTypes.any, +>any : Symbol(any, Decl(file.ts, 39, 36)) +>PropTypes.any : Symbol(PropTypes.any, Decl(index.d.ts, 24, 12)) +>PropTypes : Symbol(PropTypes, Decl(file.ts, 0, 6)) +>any : Symbol(PropTypes.any, Decl(index.d.ts, 24, 12)) + + array: PropTypes.array.isRequired, +>array : Symbol(array, Decl(file.ts, 40, 23)) +>PropTypes.array.isRequired : Symbol(PropTypes.Requireable.isRequired, Decl(index.d.ts, 13, 73)) +>PropTypes.array : Symbol(PropTypes.array, Decl(index.d.ts, 25, 12)) +>PropTypes : Symbol(PropTypes, Decl(file.ts, 0, 6)) +>array : Symbol(PropTypes.array, Decl(index.d.ts, 25, 12)) +>isRequired : Symbol(PropTypes.Requireable.isRequired, Decl(index.d.ts, 13, 73)) + + bool: PropTypes.bool.isRequired, +>bool : Symbol(bool, Decl(file.ts, 41, 38)) +>PropTypes.bool.isRequired : Symbol(PropTypes.Requireable.isRequired, Decl(index.d.ts, 13, 73)) +>PropTypes.bool : Symbol(PropTypes.bool, Decl(index.d.ts, 26, 12)) +>PropTypes : Symbol(PropTypes, Decl(file.ts, 0, 6)) +>bool : Symbol(PropTypes.bool, Decl(index.d.ts, 26, 12)) +>isRequired : Symbol(PropTypes.Requireable.isRequired, Decl(index.d.ts, 13, 73)) + + shape: PropTypes.shape(innerProps).isRequired, +>shape : Symbol(shape, Decl(file.ts, 42, 36)) +>PropTypes.shape(innerProps).isRequired : Symbol(PropTypes.Requireable.isRequired, Decl(index.d.ts, 13, 73)) +>PropTypes.shape : Symbol(PropTypes.shape, Decl(index.d.ts, 28, 41)) +>PropTypes : Symbol(PropTypes, Decl(file.ts, 0, 6)) +>shape : Symbol(PropTypes.shape, Decl(index.d.ts, 28, 41)) +>innerProps : Symbol(innerProps, Decl(file.ts, 18, 5)) +>isRequired : Symbol(PropTypes.Requireable.isRequired, Decl(index.d.ts, 13, 73)) + + oneOfType: PropTypes.oneOfType(arrayOfTypes).isRequired, +>oneOfType : Symbol(oneOfType, Decl(file.ts, 43, 50)) +>PropTypes.oneOfType(arrayOfTypes).isRequired : Symbol(PropTypes.Requireable.isRequired, Decl(index.d.ts, 13, 73)) +>PropTypes.oneOfType : Symbol(PropTypes.oneOfType, Decl(index.d.ts, 29, 89)) +>PropTypes : Symbol(PropTypes, Decl(file.ts, 0, 6)) +>oneOfType : Symbol(PropTypes.oneOfType, Decl(index.d.ts, 29, 89)) +>arrayOfTypes : Symbol(arrayOfTypes, Decl(file.ts, 24, 5)) +>isRequired : Symbol(PropTypes.Requireable.isRequired, Decl(index.d.ts, 13, 73)) + +}; + +type ExtractedProps = PropTypes.InferProps; +>ExtractedProps : Symbol(ExtractedProps, Decl(file.ts, 45, 2)) +>PropTypes : Symbol(PropTypes, Decl(file.ts, 0, 6)) +>InferProps : Symbol(PropTypes.InferProps, Decl(index.d.ts, 19, 66)) +>propTypes : Symbol(propTypes, Decl(file.ts, 30, 5)) + +type ExtractedPropsWithoutAnnotation = PropTypes.InferProps; +>ExtractedPropsWithoutAnnotation : Symbol(ExtractedPropsWithoutAnnotation, Decl(file.ts, 47, 61)) +>PropTypes : Symbol(PropTypes, Decl(file.ts, 0, 6)) +>InferProps : Symbol(PropTypes.InferProps, Decl(index.d.ts, 19, 66)) +>propTypesWithoutAnnotation : Symbol(propTypesWithoutAnnotation, Decl(file.ts, 39, 5)) + +type ExtractPropsMatch = ExtractedProps extends ExtractedPropsWithoutAnnotation ? true : false; +>ExtractPropsMatch : Symbol(ExtractPropsMatch, Decl(file.ts, 49, 95)) +>ExtractedProps : Symbol(ExtractedProps, Decl(file.ts, 45, 2)) +>ExtractedPropsWithoutAnnotation : Symbol(ExtractedPropsWithoutAnnotation, Decl(file.ts, 47, 61)) + +const x: true = (null as any as ExtractPropsMatch); +>x : Symbol(x, Decl(file.ts, 52, 5)) +>ExtractPropsMatch : Symbol(ExtractPropsMatch, Decl(file.ts, 49, 95)) + diff --git a/tests/baselines/reference/propTypeValidatorInference.types b/tests/baselines/reference/propTypeValidatorInference.types new file mode 100644 index 0000000000000..eb292dac5c486 --- /dev/null +++ b/tests/baselines/reference/propTypeValidatorInference.types @@ -0,0 +1,301 @@ +=== tests/cases/compiler/node_modules/prop-types/index.d.ts === +export const nominalTypeHack: unique symbol; +>nominalTypeHack : unique symbol + +export type IsOptional = undefined | null extends T ? true : undefined extends T ? true : null extends T ? true : false; +>IsOptional : IsOptional +>null : null +>true : true +>true : true +>null : null +>true : true +>false : false + +export type RequiredKeys = { [K in keyof V]-?: Exclude extends Validator ? IsOptional extends true ? never : K : never }[keyof V]; +>RequiredKeys : { [K in keyof V]-?: Exclude extends Validator ? IsOptional extends true ? never : K : never; }[keyof V] +>true : true + +export type OptionalKeys = Exclude>; +>OptionalKeys : Exclude extends Validator ? IsOptional extends true ? never : K : never; }[keyof V]> + +export type InferPropsInner = { [K in keyof V]-?: InferType; }; +>InferPropsInner : InferPropsInner + +export interface Validator { + (props: object, propName: string, componentName: string, location: string, propFullName: string): Error | null; +>props : object +>propName : string +>componentName : string +>location : string +>propFullName : string +>null : null + + [nominalTypeHack]?: T; +>[nominalTypeHack] : T | undefined +>nominalTypeHack : unique symbol +} + +export interface Requireable extends Validator { +>null : null + + isRequired: Validator>; +>isRequired : Validator> +} + +export type ValidationMap = { [K in keyof T]?: Validator }; +>ValidationMap : ValidationMap + +export type InferType = V extends Validator ? T : any; +>InferType : InferType + +export type InferProps = +>InferProps : InferProps + + & InferPropsInner>> + & Partial>>>; + +export const any: Requireable; +>any : Requireable + +export const array: Requireable; +>array : Requireable + +export const bool: Requireable; +>bool : Requireable + +export const string: Requireable; +>string : Requireable + +export const number: Requireable; +>number : Requireable + +export function shape

>(type: P): Requireable>; +>shape :

>(type: P) => Requireable> +>type : P + +export function oneOfType>(types: T[]): Requireable>>; +>oneOfType : >(types: T[]) => Requireable>> +>types : T[] + + +=== tests/cases/compiler/file.ts === +import * as PropTypes from "prop-types"; +>PropTypes : typeof PropTypes + +interface Props { + any?: any; +>any : any + + array: string[]; +>array : string[] + + bool: boolean; +>bool : boolean + + shape: { +>shape : { foo: string; bar?: boolean | undefined; baz?: any; } + + foo: string; +>foo : string + + bar?: boolean; +>bar : boolean | undefined + + baz?: any +>baz : any + + }; + oneOfType: string | boolean | { +>oneOfType : string | boolean | { foo?: string | undefined; bar: number; } + + foo?: string; +>foo : string | undefined + + bar: number; +>bar : number + + }; +} + +type PropTypesMap = PropTypes.ValidationMap; +>PropTypesMap : PropTypes.ValidationMap +>PropTypes : any + +const innerProps = { +>innerProps : { foo: PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; } +>{ foo: PropTypes.string.isRequired, bar: PropTypes.bool, baz: PropTypes.any} : { foo: PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; } + + foo: PropTypes.string.isRequired, +>foo : PropTypes.Validator +>PropTypes.string.isRequired : PropTypes.Validator +>PropTypes.string : PropTypes.Requireable +>PropTypes : typeof PropTypes +>string : PropTypes.Requireable +>isRequired : PropTypes.Validator + + bar: PropTypes.bool, +>bar : PropTypes.Requireable +>PropTypes.bool : PropTypes.Requireable +>PropTypes : typeof PropTypes +>bool : PropTypes.Requireable + + baz: PropTypes.any +>baz : PropTypes.Requireable +>PropTypes.any : PropTypes.Requireable +>PropTypes : typeof PropTypes +>any : PropTypes.Requireable + +}; + +const arrayOfTypes = [PropTypes.string, PropTypes.bool, PropTypes.shape({ +>arrayOfTypes : (PropTypes.Requireable | PropTypes.Requireable | PropTypes.Requireable; bar: PropTypes.Validator; }>>)[] +>[PropTypes.string, PropTypes.bool, PropTypes.shape({ foo: PropTypes.string, bar: PropTypes.number.isRequired})] : (PropTypes.Requireable | PropTypes.Requireable | PropTypes.Requireable; bar: PropTypes.Validator; }>>)[] +>PropTypes.string : PropTypes.Requireable +>PropTypes : typeof PropTypes +>string : PropTypes.Requireable +>PropTypes.bool : PropTypes.Requireable +>PropTypes : typeof PropTypes +>bool : PropTypes.Requireable +>PropTypes.shape({ foo: PropTypes.string, bar: PropTypes.number.isRequired}) : PropTypes.Requireable; bar: PropTypes.Validator; }>> +>PropTypes.shape :

>(type: P) => PropTypes.Requireable> +>PropTypes : typeof PropTypes +>shape :

>(type: P) => PropTypes.Requireable> +>{ foo: PropTypes.string, bar: PropTypes.number.isRequired} : { foo: PropTypes.Requireable; bar: PropTypes.Validator; } + + foo: PropTypes.string, +>foo : PropTypes.Requireable +>PropTypes.string : PropTypes.Requireable +>PropTypes : typeof PropTypes +>string : PropTypes.Requireable + + bar: PropTypes.number.isRequired +>bar : PropTypes.Validator +>PropTypes.number.isRequired : PropTypes.Validator +>PropTypes.number : PropTypes.Requireable +>PropTypes : typeof PropTypes +>number : PropTypes.Requireable +>isRequired : PropTypes.Validator + +})]; + +// TS checking +const propTypes: PropTypesMap = { +>propTypes : PropTypes.ValidationMap +>{ any: PropTypes.any, array: PropTypes.array.isRequired, bool: PropTypes.bool.isRequired, shape: PropTypes.shape(innerProps).isRequired, oneOfType: PropTypes.oneOfType(arrayOfTypes).isRequired,} : { any: PropTypes.Requireable; array: PropTypes.Validator; bool: PropTypes.Validator; shape: PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; }>>; oneOfType: PropTypes.Validator; bar: PropTypes.Validator; }>>; } + + any: PropTypes.any, +>any : PropTypes.Requireable +>PropTypes.any : PropTypes.Requireable +>PropTypes : typeof PropTypes +>any : PropTypes.Requireable + + array: PropTypes.array.isRequired, +>array : PropTypes.Validator +>PropTypes.array.isRequired : PropTypes.Validator +>PropTypes.array : PropTypes.Requireable +>PropTypes : typeof PropTypes +>array : PropTypes.Requireable +>isRequired : PropTypes.Validator + + bool: PropTypes.bool.isRequired, +>bool : PropTypes.Validator +>PropTypes.bool.isRequired : PropTypes.Validator +>PropTypes.bool : PropTypes.Requireable +>PropTypes : typeof PropTypes +>bool : PropTypes.Requireable +>isRequired : PropTypes.Validator + + shape: PropTypes.shape(innerProps).isRequired, +>shape : PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; }>> +>PropTypes.shape(innerProps).isRequired : PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; }>> +>PropTypes.shape(innerProps) : PropTypes.Requireable; bar: PropTypes.Requireable; baz: PropTypes.Requireable; }>> +>PropTypes.shape :

>(type: P) => PropTypes.Requireable> +>PropTypes : typeof PropTypes +>shape :

>(type: P) => PropTypes.Requireable> +>innerProps : { foo: PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; } +>isRequired : PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; }>> + + oneOfType: PropTypes.oneOfType(arrayOfTypes).isRequired, +>oneOfType : PropTypes.Validator; bar: PropTypes.Validator; }>> +>PropTypes.oneOfType(arrayOfTypes).isRequired : PropTypes.Validator; bar: PropTypes.Validator; }>> +>PropTypes.oneOfType(arrayOfTypes) : PropTypes.Requireable; bar: PropTypes.Validator; }>> +>PropTypes.oneOfType : >(types: T[]) => PropTypes.Requireable>> +>PropTypes : typeof PropTypes +>oneOfType : >(types: T[]) => PropTypes.Requireable>> +>arrayOfTypes : (PropTypes.Requireable | PropTypes.Requireable | PropTypes.Requireable; bar: PropTypes.Validator; }>>)[] +>isRequired : PropTypes.Validator; bar: PropTypes.Validator; }>> + +}; + +// JS checking +const propTypesWithoutAnnotation = { +>propTypesWithoutAnnotation : { any: PropTypes.Requireable; array: PropTypes.Validator; bool: PropTypes.Validator; shape: PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; }>>; oneOfType: PropTypes.Validator; bar: PropTypes.Validator; }>>; } +>{ any: PropTypes.any, array: PropTypes.array.isRequired, bool: PropTypes.bool.isRequired, shape: PropTypes.shape(innerProps).isRequired, oneOfType: PropTypes.oneOfType(arrayOfTypes).isRequired,} : { any: PropTypes.Requireable; array: PropTypes.Validator; bool: PropTypes.Validator; shape: PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; }>>; oneOfType: PropTypes.Validator; bar: PropTypes.Validator; }>>; } + + any: PropTypes.any, +>any : PropTypes.Requireable +>PropTypes.any : PropTypes.Requireable +>PropTypes : typeof PropTypes +>any : PropTypes.Requireable + + array: PropTypes.array.isRequired, +>array : PropTypes.Validator +>PropTypes.array.isRequired : PropTypes.Validator +>PropTypes.array : PropTypes.Requireable +>PropTypes : typeof PropTypes +>array : PropTypes.Requireable +>isRequired : PropTypes.Validator + + bool: PropTypes.bool.isRequired, +>bool : PropTypes.Validator +>PropTypes.bool.isRequired : PropTypes.Validator +>PropTypes.bool : PropTypes.Requireable +>PropTypes : typeof PropTypes +>bool : PropTypes.Requireable +>isRequired : PropTypes.Validator + + shape: PropTypes.shape(innerProps).isRequired, +>shape : PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; }>> +>PropTypes.shape(innerProps).isRequired : PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; }>> +>PropTypes.shape(innerProps) : PropTypes.Requireable; bar: PropTypes.Requireable; baz: PropTypes.Requireable; }>> +>PropTypes.shape :

>(type: P) => PropTypes.Requireable> +>PropTypes : typeof PropTypes +>shape :

>(type: P) => PropTypes.Requireable> +>innerProps : { foo: PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; } +>isRequired : PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; }>> + + oneOfType: PropTypes.oneOfType(arrayOfTypes).isRequired, +>oneOfType : PropTypes.Validator; bar: PropTypes.Validator; }>> +>PropTypes.oneOfType(arrayOfTypes).isRequired : PropTypes.Validator; bar: PropTypes.Validator; }>> +>PropTypes.oneOfType(arrayOfTypes) : PropTypes.Requireable; bar: PropTypes.Validator; }>> +>PropTypes.oneOfType : >(types: T[]) => PropTypes.Requireable>> +>PropTypes : typeof PropTypes +>oneOfType : >(types: T[]) => PropTypes.Requireable>> +>arrayOfTypes : (PropTypes.Requireable | PropTypes.Requireable | PropTypes.Requireable; bar: PropTypes.Validator; }>>)[] +>isRequired : PropTypes.Validator; bar: PropTypes.Validator; }>> + +}; + +type ExtractedProps = PropTypes.InferProps; +>ExtractedProps : PropTypes.InferProps> +>PropTypes : any +>propTypes : PropTypes.ValidationMap + +type ExtractedPropsWithoutAnnotation = PropTypes.InferProps; +>ExtractedPropsWithoutAnnotation : PropTypes.InferProps<{ any: PropTypes.Requireable; array: PropTypes.Validator; bool: PropTypes.Validator; shape: PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; }>>; oneOfType: PropTypes.Validator; bar: PropTypes.Validator; }>>; }> +>PropTypes : any +>propTypesWithoutAnnotation : { any: PropTypes.Requireable; array: PropTypes.Validator; bool: PropTypes.Validator; shape: PropTypes.Validator; bar: PropTypes.Requireable; baz: PropTypes.Requireable; }>>; oneOfType: PropTypes.Validator; bar: PropTypes.Validator; }>>; } + +type ExtractPropsMatch = ExtractedProps extends ExtractedPropsWithoutAnnotation ? true : false; +>ExtractPropsMatch : true +>true : true +>false : false + +const x: true = (null as any as ExtractPropsMatch); +>x : true +>true : true +>(null as any as ExtractPropsMatch) : true +>null as any as ExtractPropsMatch : true +>null as any : any +>null : null + diff --git a/tests/cases/compiler/propTypeValidatorInference.ts b/tests/cases/compiler/propTypeValidatorInference.ts new file mode 100644 index 0000000000000..54030ab91a916 --- /dev/null +++ b/tests/cases/compiler/propTypeValidatorInference.ts @@ -0,0 +1,89 @@ +// @strict: true +// @filename: node_modules/prop-types/index.d.ts +export const nominalTypeHack: unique symbol; + +export type IsOptional = undefined | null extends T ? true : undefined extends T ? true : null extends T ? true : false; + +export type RequiredKeys = { [K in keyof V]-?: Exclude extends Validator ? IsOptional extends true ? never : K : never }[keyof V]; +export type OptionalKeys = Exclude>; +export type InferPropsInner = { [K in keyof V]-?: InferType; }; + +export interface Validator { + (props: object, propName: string, componentName: string, location: string, propFullName: string): Error | null; + [nominalTypeHack]?: T; +} + +export interface Requireable extends Validator { + isRequired: Validator>; +} + +export type ValidationMap = { [K in keyof T]?: Validator }; + +export type InferType = V extends Validator ? T : any; +export type InferProps = + & InferPropsInner>> + & Partial>>>; + +export const any: Requireable; +export const array: Requireable; +export const bool: Requireable; +export const string: Requireable; +export const number: Requireable; +export function shape

>(type: P): Requireable>; +export function oneOfType>(types: T[]): Requireable>>; + + +// @filename: file.ts +import * as PropTypes from "prop-types"; +interface Props { + any?: any; + array: string[]; + bool: boolean; + shape: { + foo: string; + bar?: boolean; + baz?: any + }; + oneOfType: string | boolean | { + foo?: string; + bar: number; + }; +} + +type PropTypesMap = PropTypes.ValidationMap; + +const innerProps = { + foo: PropTypes.string.isRequired, + bar: PropTypes.bool, + baz: PropTypes.any +}; + +const arrayOfTypes = [PropTypes.string, PropTypes.bool, PropTypes.shape({ + foo: PropTypes.string, + bar: PropTypes.number.isRequired +})]; + +// TS checking +const propTypes: PropTypesMap = { + any: PropTypes.any, + array: PropTypes.array.isRequired, + bool: PropTypes.bool.isRequired, + shape: PropTypes.shape(innerProps).isRequired, + oneOfType: PropTypes.oneOfType(arrayOfTypes).isRequired, +}; + +// JS checking +const propTypesWithoutAnnotation = { + any: PropTypes.any, + array: PropTypes.array.isRequired, + bool: PropTypes.bool.isRequired, + shape: PropTypes.shape(innerProps).isRequired, + oneOfType: PropTypes.oneOfType(arrayOfTypes).isRequired, +}; + +type ExtractedProps = PropTypes.InferProps; + +type ExtractedPropsWithoutAnnotation = PropTypes.InferProps; + +type ExtractPropsMatch = ExtractedProps extends ExtractedPropsWithoutAnnotation ? true : false; +const x: true = (null as any as ExtractPropsMatch); \ No newline at end of file From 8268eeb9de2d25abf2509acfe7703450f167c58f Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 7 Mar 2019 15:12:09 -0800 Subject: [PATCH 11/13] Fix lint --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d7e512d865176..7c604f4808531 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11189,7 +11189,7 @@ namespace ts { // which is redundant - we'll produce new type identities, but all type params have already been mapped. // This also gives us a way to detect restrictive instances upon comparisons and _disable_ the "distributeive constraint" // assignability check for them, which is distinctly unsafe, as once you have a restrctive instance, all the type parameters - // are constrained to `unknown` and produce tons of false positives/negatives! + // are constrained to `unknown` and produce tons of false positives/negatives! type.restrictiveInstantiation.restrictiveInstantiation = type.restrictiveInstantiation; return type.restrictiveInstantiation; } From 5896af393fbfb2841f508f5f6bde838ada0e7e3b Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Fri, 8 Mar 2019 14:22:50 -0800 Subject: [PATCH 12/13] Factor logic into worker vs cacheing function --- src/compiler/checker.ts | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7c604f4808531..30b3d8395c9cf 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -10102,34 +10102,37 @@ namespace ts { if (checkType === wildcardType || extendsType === wildcardType) { return wildcardType; } - // Simplifications for types of the form `T extends U ? T : never` and `T extends U ? never : T`. const trueType = instantiateType(root.trueType, mapper); const falseType = instantiateType(root.falseType, mapper); const instantiationId = `${root.isDistributive ? "d" : ""}${getTypeId(checkType)}>${getTypeId(extendsType)}?${getTypeId(trueType)}:${getTypeId(falseType)}`; - let result = conditionalTypes.get(instantiationId); + const result = conditionalTypes.get(instantiationId); if (result) { return result; } + const newResult = getConditionalTypeWorker(root, mapper, checkType, extendsType, trueType, falseType); + conditionalTypes.set(instantiationId, newResult); + return newResult; + } + + function getConditionalTypeWorker(root: ConditionalRoot, mapper: TypeMapper | undefined, checkType: Type, extendsType: Type, trueType: Type, falseType: Type) { + // Simplifications for types of the form `T extends U ? T : never` and `T extends U ? never : T`. if (falseType.flags & TypeFlags.Never && isTypeIdenticalTo(getActualTypeVariable(trueType), getActualTypeVariable(checkType))) { if (checkType.flags & TypeFlags.Any || isTypeAssignableTo(getRestrictiveInstantiation(checkType), getRestrictiveInstantiation(extendsType))) { // Always true - result = getDefaultConstraintOfTrueBranchOfConditionalType(root, /*combinedMapper*/ undefined, mapper); + return getDefaultConstraintOfTrueBranchOfConditionalType(root, /*combinedMapper*/ undefined, mapper); } else if (isIntersectionEmpty(checkType, extendsType)) { // Always false - result = neverType; + return neverType; } } else if (trueType.flags & TypeFlags.Never && isTypeIdenticalTo(getActualTypeVariable(falseType), getActualTypeVariable(checkType))) { if (!(checkType.flags & TypeFlags.Any) && isTypeAssignableTo(getRestrictiveInstantiation(checkType), getRestrictiveInstantiation(extendsType))) { // Always true - result = neverType; + return neverType; } else if (checkType.flags & TypeFlags.Any || isIntersectionEmpty(checkType, extendsType)) { // Always false - result = falseType; // TODO: Intersect negated `extends` type here + return falseType; // TODO: Intersect negated `extends` type here } } - if (result) { - conditionalTypes.set(instantiationId, result); - return result; - } + const checkTypeInstantiable = maybeTypeOfKind(checkType, TypeFlags.Instantiable | TypeFlags.GenericMappedType); let combinedMapper: TypeMapper | undefined; if (root.inferTypeParameters) { @@ -10147,21 +10150,17 @@ namespace ts { // We attempt to resolve the conditional type only when the check and extends types are non-generic if (!checkTypeInstantiable && !maybeTypeOfKind(inferredExtendsType, TypeFlags.Instantiable | TypeFlags.GenericMappedType)) { if (inferredExtendsType.flags & TypeFlags.AnyOrUnknown) { - conditionalTypes.set(instantiationId, trueType); return trueType; } // Return union of trueType and falseType for 'any' since it matches anything if (checkType.flags & TypeFlags.Any) { - result = getUnionType([instantiateType(root.trueType, combinedMapper || mapper), falseType]); - conditionalTypes.set(instantiationId, result); - return result; + return getUnionType([instantiateType(root.trueType, combinedMapper || mapper), falseType]); } // Return falseType for a definitely false extends check. We check an instantiations of the two // types with type parameters mapped to the wildcard type, the most permissive instantiations // possible (the wildcard type is assignable to and from all types). If those are not related, // then no instantiations will be and we can just return the false branch type. if (!isTypeAssignableTo(getPermissiveInstantiation(checkType), getPermissiveInstantiation(inferredExtendsType))) { - conditionalTypes.set(instantiationId, falseType); return falseType; } // Return trueType for a definitely true extends check. We check instantiations of the two @@ -10170,14 +10169,12 @@ namespace ts { // type Foo = T extends { x: string } ? string : number // doesn't immediately resolve to 'string' instead of being deferred. if (isTypeAssignableTo(getRestrictiveInstantiation(checkType), getRestrictiveInstantiation(inferredExtendsType))) { - result = instantiateType(root.trueType, combinedMapper || mapper); - conditionalTypes.set(instantiationId, result); - return result; + return instantiateType(root.trueType, combinedMapper || mapper); } } // Return a deferred type for a check that is neither definitely true nor definitely false const erasedCheckType = getActualTypeVariable(checkType); - result = createType(TypeFlags.Conditional); + const result = createType(TypeFlags.Conditional); (result as ConditionalType).root = root; (result as ConditionalType).checkType = erasedCheckType; (result as ConditionalType).extendsType = extendsType; @@ -10189,7 +10186,6 @@ namespace ts { } result.aliasSymbol = root.aliasSymbol; result.aliasTypeArguments = instantiateTypes(root.aliasTypeArguments, mapper!); // TODO: GH#18217 - conditionalTypes.set(instantiationId, result); return result; } From 80bceda42178e7f7b14c0a549bc0d2526461cd3f Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Fri, 8 Mar 2019 14:38:41 -0800 Subject: [PATCH 13/13] Remove now unneeded casts --- src/compiler/checker.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 30b3d8395c9cf..ae4c006e5c3cf 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -10175,14 +10175,14 @@ namespace ts { // Return a deferred type for a check that is neither definitely true nor definitely false const erasedCheckType = getActualTypeVariable(checkType); const result = createType(TypeFlags.Conditional); - (result as ConditionalType).root = root; - (result as ConditionalType).checkType = erasedCheckType; - (result as ConditionalType).extendsType = extendsType; - (result as ConditionalType).mapper = mapper; - (result as ConditionalType).combinedMapper = combinedMapper; + result.root = root; + result.checkType = erasedCheckType; + result.extendsType = extendsType; + result.mapper = mapper; + result.combinedMapper = combinedMapper; if (!combinedMapper) { - (result as ConditionalType).resolvedTrueType = trueType; - (result as ConditionalType).resolvedFalseType = falseType; + result.resolvedTrueType = trueType; + result.resolvedFalseType = falseType; } result.aliasSymbol = root.aliasSymbol; result.aliasTypeArguments = instantiateTypes(root.aliasTypeArguments, mapper!); // TODO: GH#18217