Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Defer resolution of true and false branches in conditional types #31354

Merged
merged 7 commits into from May 14, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
134 changes: 63 additions & 71 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions src/compiler/types.ts
Expand Up @@ -3966,6 +3966,8 @@ namespace ts {
StructuredOrInstantiable = StructuredType | Instantiable,
/* @internal */
ObjectFlagsType = Nullable | Never | Object | Union | Intersection,
/* @internal */
Simplifiable = IndexedAccess | Conditional | Substitution,
// 'Narrowable' types are types where narrowing actually narrows.
// This *should* be every type other than null, undefined, void, and never
Narrowable = Any | Unknown | StructuredOrInstantiable | StringLike | NumberLike | BigIntLike | BooleanLike | ESSymbol | UniqueESSymbol | NonPrimitive,
Expand Down Expand Up @@ -4338,8 +4340,8 @@ namespace ts {
root: ConditionalRoot;
checkType: Type;
extendsType: Type;
trueType: Type;
falseType: Type;
resolvedTrueType: Type;
resolvedFalseType: Type;
/* @internal */
resolvedInferredTrueType?: Type; // The `trueType` instantiated with the `combinedMapper`, if present
/* @internal */
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/api/tsserverlibrary.d.ts
Expand Up @@ -2390,8 +2390,8 @@ declare namespace ts {
root: ConditionalRoot;
checkType: Type;
extendsType: Type;
trueType: Type;
falseType: Type;
resolvedTrueType: Type;
resolvedFalseType: Type;
}
interface SubstitutionType extends InstantiableType {
typeVariable: TypeVariable;
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/api/typescript.d.ts
Expand Up @@ -2390,8 +2390,8 @@ declare namespace ts {
root: ConditionalRoot;
checkType: Type;
extendsType: Type;
trueType: Type;
falseType: Type;
resolvedTrueType: Type;
resolvedFalseType: Type;
}
interface SubstitutionType extends InstantiableType {
typeVariable: TypeVariable;
Expand Down

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion tests/baselines/reference/conditionalTypes1.js
Expand Up @@ -645,7 +645,7 @@ declare type T82 = Eq2<false, true>;
declare type T83 = Eq2<false, false>;
declare type Foo<T> = T extends string ? boolean : number;
declare type Bar<T> = T extends string ? boolean : number;
declare const convert: <U>(value: Foo<U>) => Foo<U>;
declare const convert: <U>(value: Foo<U>) => Bar<U>;
declare type Baz<T> = Foo<T>;
declare const convert2: <T>(value: Foo<T>) => Foo<T>;
declare function f31<T>(): void;
Expand Down
6 changes: 3 additions & 3 deletions tests/baselines/reference/conditionalTypes1.types
Expand Up @@ -779,8 +779,8 @@ type Bar<T> = T extends string ? boolean : number;
>Bar : Bar<T>

const convert = <U>(value: Foo<U>): Bar<U> => value;
>convert : <U>(value: Foo<U>) => Foo<U>
><U>(value: Foo<U>): Bar<U> => value : <U>(value: Foo<U>) => Foo<U>
>convert : <U>(value: Foo<U>) => Bar<U>
><U>(value: Foo<U>): Bar<U> => value : <U>(value: Foo<U>) => Bar<U>
>value : Foo<U>
>value : Foo<U>

Expand Down Expand Up @@ -832,7 +832,7 @@ function f33<T, U>() {
>T1 : Foo<T & U>

type T2 = Bar<T & U>;
>T2 : Foo<T & U>
>T2 : Bar<T & U>

var z: T1;
>z : Foo<T & U>
Expand Down
28 changes: 14 additions & 14 deletions tests/baselines/reference/conditionalTypes2.errors.txt
Expand Up @@ -10,13 +10,13 @@ tests/cases/conformance/types/conditional/conditionalTypes2.ts(24,5): error TS23
Type 'keyof B' is not assignable to type 'keyof A'.
Type 'string | number | symbol' is not assignable to type 'keyof A'.
Type 'string' is not assignable to type 'keyof A'.
Type 'string' is not assignable to type 'number | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf"'.
Type 'keyof B' is not assignable to type 'number | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf"'.
Type 'string | number | symbol' is not assignable to type 'number | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf"'.
Type 'string' is not assignable to type 'number | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf"'.
Type 'keyof B' is not assignable to type '"valueOf"'.
Type 'string | number | symbol' is not assignable to type '"valueOf"'.
Type 'string' is not assignable to type '"valueOf"'.
Type 'string' is not assignable to type 'number | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf" | keyof A'.
Copy link
Member

@weswigham weswigham May 13, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

did keyof A expand into... itself? o.O

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the first keyof A, A is a substitution type. It then gets expanded into keyof (A & string) which becomes the union that ends in keyof A where A is the actual type variable. Not ideal, but neither was the prior error message.

Type 'keyof B' is not assignable to type 'number | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf" | keyof A'.
Type 'string | number | symbol' is not assignable to type 'number | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf" | keyof A'.
Type 'string' is not assignable to type 'number | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf" | keyof A'.
Type 'keyof B' is not assignable to type 'keyof A'.
Type 'string | number | symbol' is not assignable to type 'keyof A'.
Type 'string' is not assignable to type 'keyof A'.
tests/cases/conformance/types/conditional/conditionalTypes2.ts(25,5): error TS2322: Type 'Invariant<A>' is not assignable to type 'Invariant<B>'.
Types of property 'foo' are incompatible.
Type 'A extends string ? keyof A : A' is not assignable to type 'B extends string ? keyof B : B'.
Expand Down Expand Up @@ -73,13 +73,13 @@ tests/cases/conformance/types/conditional/conditionalTypes2.ts(75,12): error TS2
!!! error TS2322: Type 'keyof B' is not assignable to type 'keyof A'.
!!! error TS2322: Type 'string | number | symbol' is not assignable to type 'keyof A'.
!!! error TS2322: Type 'string' is not assignable to type 'keyof A'.
!!! error TS2322: Type 'string' is not assignable to type 'number | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf"'.
!!! error TS2322: Type 'keyof B' is not assignable to type 'number | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf"'.
!!! error TS2322: Type 'string | number | symbol' is not assignable to type 'number | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf"'.
!!! error TS2322: Type 'string' is not assignable to type 'number | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf"'.
!!! error TS2322: Type 'keyof B' is not assignable to type '"valueOf"'.
!!! error TS2322: Type 'string | number | symbol' is not assignable to type '"valueOf"'.
!!! error TS2322: Type 'string' is not assignable to type '"valueOf"'.
!!! error TS2322: Type 'string' is not assignable to type 'number | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf" | keyof A'.
!!! error TS2322: Type 'keyof B' is not assignable to type 'number | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf" | keyof A'.
!!! error TS2322: Type 'string | number | symbol' is not assignable to type 'number | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf" | keyof A'.
!!! error TS2322: Type 'string' is not assignable to type 'number | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf" | keyof A'.
!!! error TS2322: Type 'keyof B' is not assignable to type 'keyof A'.
!!! error TS2322: Type 'string | number | symbol' is not assignable to type 'keyof A'.
!!! error TS2322: Type 'string' is not assignable to type 'keyof A'.
b = a; // Error
~
!!! error TS2322: Type 'Invariant<A>' is not assignable to type 'Invariant<B>'.
Expand Down
96 changes: 48 additions & 48 deletions tests/baselines/reference/conditionalTypesSimplifyWhenTrivial.types
@@ -1,49 +1,49 @@
=== tests/cases/compiler/conditionalTypesSimplifyWhenTrivial.ts ===
const fn1 = <Params>(
>fn1 : <Params>(params: Pick<Params, keyof Params>) => Params
><Params>( params: Pick<Params, Exclude<keyof Params, never>>,): Params => params : <Params>(params: Pick<Params, keyof Params>) => Params
>fn1 : <Params>(params: Pick<Params, Exclude<keyof Params, never>>) => Params
><Params>( params: Pick<Params, Exclude<keyof Params, never>>,): Params => params : <Params>(params: Pick<Params, Exclude<keyof Params, never>>) => Params

params: Pick<Params, Exclude<keyof Params, never>>,
>params : Pick<Params, keyof Params>
>params : Pick<Params, Exclude<keyof Params, never>>

): Params => params;
>params : Pick<Params, keyof Params>
>params : Pick<Params, Exclude<keyof Params, never>>

function fn2<T>(x: Exclude<T, never>) {
>fn2 : <T>(x: T) => void
>x : T
>fn2 : <T>(x: Exclude<T, never>) => void
>x : Exclude<T, never>

var y: T = x;
>y : T
>x : T
>x : Exclude<T, never>

x = y;
>x = y : T
>x : T
>x : Exclude<T, never>
>y : T
}

const fn3 = <Params>(
>fn3 : <Params>(params: Pick<Params, keyof Params>) => Params
><Params>( params: Pick<Params, Extract<keyof Params, keyof Params>>,): Params => params : <Params>(params: Pick<Params, keyof Params>) => Params
>fn3 : <Params>(params: Pick<Params, Extract<keyof Params, keyof Params>>) => Params
><Params>( params: Pick<Params, Extract<keyof Params, keyof Params>>,): Params => params : <Params>(params: Pick<Params, Extract<keyof Params, keyof Params>>) => Params

params: Pick<Params, Extract<keyof Params, keyof Params>>,
>params : Pick<Params, keyof Params>
>params : Pick<Params, Extract<keyof Params, keyof Params>>

): Params => params;
>params : Pick<Params, keyof Params>
>params : Pick<Params, Extract<keyof Params, keyof Params>>

function fn4<T>(x: Extract<T, T>) {
>fn4 : <T>(x: T) => void
>x : T
>fn4 : <T>(x: Extract<T, T>) => void
>x : Extract<T, T>

var y: T = x;
>y : T
>x : T
>x : Extract<T, T>

x = y;
>x = y : T
>x : T
>x : Extract<T, T>
>y : T
}

Expand All @@ -57,101 +57,101 @@ type ExcludeWithDefault<T, U, D = never> = T extends U ? D : T;
>ExcludeWithDefault : ExcludeWithDefault<T, U, D>

const fn5 = <Params>(
>fn5 : <Params>(params: Pick<Params, keyof Params>) => Params
><Params>( params: Pick<Params, ExcludeWithDefault<keyof Params, never>>,): Params => params : <Params>(params: Pick<Params, keyof Params>) => Params
>fn5 : <Params>(params: Pick<Params, ExcludeWithDefault<keyof Params, never, never>>) => Params
><Params>( params: Pick<Params, ExcludeWithDefault<keyof Params, never>>,): Params => params : <Params>(params: Pick<Params, ExcludeWithDefault<keyof Params, never, never>>) => Params

params: Pick<Params, ExcludeWithDefault<keyof Params, never>>,
>params : Pick<Params, keyof Params>
>params : Pick<Params, ExcludeWithDefault<keyof Params, never, never>>

): Params => params;
>params : Pick<Params, keyof Params>
>params : Pick<Params, ExcludeWithDefault<keyof Params, never, never>>

function fn6<T>(x: ExcludeWithDefault<T, never>) {
>fn6 : <T>(x: T) => void
>x : T
>fn6 : <T>(x: ExcludeWithDefault<T, never, never>) => void
>x : ExcludeWithDefault<T, never, never>

var y: T = x;
>y : T
>x : T
>x : ExcludeWithDefault<T, never, never>

x = y;
>x = y : T
>x : T
>x : ExcludeWithDefault<T, never, never>
>y : T
}

const fn7 = <Params>(
>fn7 : <Params>(params: Pick<Params, keyof Params>) => Params
><Params>( params: Pick<Params, ExtractWithDefault<keyof Params, keyof Params>>,): Params => params : <Params>(params: Pick<Params, keyof Params>) => Params
>fn7 : <Params>(params: Pick<Params, ExtractWithDefault<keyof Params, keyof Params, never>>) => Params
><Params>( params: Pick<Params, ExtractWithDefault<keyof Params, keyof Params>>,): Params => params : <Params>(params: Pick<Params, ExtractWithDefault<keyof Params, keyof Params, never>>) => Params

params: Pick<Params, ExtractWithDefault<keyof Params, keyof Params>>,
>params : Pick<Params, keyof Params>
>params : Pick<Params, ExtractWithDefault<keyof Params, keyof Params, never>>

): Params => params;
>params : Pick<Params, keyof Params>
>params : Pick<Params, ExtractWithDefault<keyof Params, keyof Params, never>>

function fn8<T>(x: ExtractWithDefault<T, T>) {
>fn8 : <T>(x: T) => void
>x : T
>fn8 : <T>(x: ExtractWithDefault<T, T, never>) => void
>x : ExtractWithDefault<T, T, never>

var y: T = x;
>y : T
>x : T
>x : ExtractWithDefault<T, T, never>

x = y;
>x = y : T
>x : T
>x : ExtractWithDefault<T, T, never>
>y : T
}

type TemplatedConditional<TCheck, TExtends, TTrue, TFalse> = TCheck extends TExtends ? TTrue : TFalse;
>TemplatedConditional : TemplatedConditional<TCheck, TExtends, TTrue, TFalse>

const fn9 = <Params>(
>fn9 : <Params>(params: Pick<Params, keyof Params>) => Params
><Params>( params: Pick<Params, TemplatedConditional<keyof Params, never, never, keyof Params>>,): Params => params : <Params>(params: Pick<Params, keyof Params>) => Params
>fn9 : <Params>(params: Pick<Params, TemplatedConditional<keyof Params, never, never, keyof Params>>) => Params
><Params>( params: Pick<Params, TemplatedConditional<keyof Params, never, never, keyof Params>>,): Params => params : <Params>(params: Pick<Params, TemplatedConditional<keyof Params, never, never, keyof Params>>) => Params

params: Pick<Params, TemplatedConditional<keyof Params, never, never, keyof Params>>,
>params : Pick<Params, keyof Params>
>params : Pick<Params, TemplatedConditional<keyof Params, never, never, keyof Params>>

): Params => params;
>params : Pick<Params, keyof Params>
>params : Pick<Params, TemplatedConditional<keyof Params, never, never, keyof Params>>

function fn10<T>(x: TemplatedConditional<T, never, never, T>) {
>fn10 : <T>(x: T) => void
>x : T
>fn10 : <T>(x: TemplatedConditional<T, never, never, T>) => void
>x : TemplatedConditional<T, never, never, T>

var y: T = x;
>y : T
>x : T
>x : TemplatedConditional<T, never, never, T>

x = y;
>x = y : T
>x : T
>x : TemplatedConditional<T, never, never, T>
>y : T
}

const fn11 = <Params>(
>fn11 : <Params>(params: Pick<Params, keyof Params>) => Params
><Params>( params: Pick<Params, TemplatedConditional<keyof Params, keyof Params, keyof Params, never>>,): Params => params : <Params>(params: Pick<Params, keyof Params>) => Params
>fn11 : <Params>(params: Pick<Params, TemplatedConditional<keyof Params, keyof Params, keyof Params, never>>) => Params
><Params>( params: Pick<Params, TemplatedConditional<keyof Params, keyof Params, keyof Params, never>>,): Params => params : <Params>(params: Pick<Params, TemplatedConditional<keyof Params, keyof Params, keyof Params, never>>) => Params

params: Pick<Params, TemplatedConditional<keyof Params, keyof Params, keyof Params, never>>,
>params : Pick<Params, keyof Params>
>params : Pick<Params, TemplatedConditional<keyof Params, keyof Params, keyof Params, never>>

): Params => params;
>params : Pick<Params, keyof Params>
>params : Pick<Params, TemplatedConditional<keyof Params, keyof Params, keyof Params, never>>

function fn12<T>(x: TemplatedConditional<T, T, T, never>) {
>fn12 : <T>(x: T) => void
>x : T
>fn12 : <T>(x: TemplatedConditional<T, T, T, never>) => void
>x : TemplatedConditional<T, T, T, never>

var y: T = x;
>y : T
>x : T
>x : TemplatedConditional<T, T, T, never>

x = y;
>x = y : T
>x : T
>x : TemplatedConditional<T, T, T, never>
>y : T
}

Expand Down
Expand Up @@ -31,7 +31,7 @@ class A {
>z : A[]

whereRelated< // Works // Type is same as A1, but is not assignable to type A
>whereRelated : <RF extends RelationFields = RelationFields, N extends "x" | "y" | "z" = "x" | "y" | "z", A1 extends A = RF[N] extends A[] ? RF[N][0] : never, A2 extends A = RF[N] extends A[] ? RF[N][0] : never>() => number
>whereRelated : <RF extends RelationFields = RelationFields, N extends "x" | "y" | "z" = "x" | "y" | "z", A1 extends A = RF[N] extends A[] ? RF[N][0] : never, A2 extends A = ShouldA<RF, N>>() => number

RF extends RelationFields = RelationFields,
N extends Name = Name,
Expand Down