Navigation Menu

Skip to content

Commit

Permalink
Remove bug-causing carve-out in conditional type instantiation that h…
Browse files Browse the repository at this point in the history
…opefully is no longer required (#51151)
  • Loading branch information
weswigham committed Oct 13, 2022
1 parent 37317a2 commit bdcc240
Show file tree
Hide file tree
Showing 5 changed files with 246 additions and 5 deletions.
6 changes: 1 addition & 5 deletions src/compiler/checker.ts
Expand Up @@ -16372,11 +16372,7 @@ namespace ts {
}
}
}
// We skip inference of the possible `infer` types unles the `extendsType` _is_ an infer type
// if it was, it's trivial to say that extendsType = checkType, however such a pattern is used to
// "reset" the type being build up during constraint calculation and avoid making an apparently "infinite" constraint
// so in those cases we refain from performing inference and retain the uninfered type parameter
if (!checkTypeInstantiable || !some(root.inferTypeParameters, t => t === extendsType)) {
if (!checkTypeInstantiable) {
// We don't want inferences from constraints as they may cause us to eagerly resolve the
// conditional type instead of deferring resolution. Also, we always want strict function
// types rules (i.e. proper contravariance) for inferences.
Expand Down
@@ -0,0 +1,33 @@
//// [recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts]
export {}
export interface Option<T> {
zip1<O extends Array<Option<any>>>(...others: O): Option<[T, ...UnzipOptionArray1<O>]>;

zip2<O extends Array<Option<any>>>(...others: O): Option<[T, ...UnzipOptionArray2<O>]>;

zip3<O extends Array<Option<any>>>(...others: O): Option<[T, ...UnzipOptionArray3<O>]>;
}

type UnzipOption<T> = T extends Option<infer V> ? V : never;

/// This doesn't work
type UnzipOptionArray1<T> = { [k in keyof T]: T[k] extends Option<any> ? UnzipOption<T[k]> : never };

/// But these work
type UnzipOptionArray2<T> = { [k in keyof T]: UnzipOption<T[k]> };
type UnzipOptionArray3<T> = { [k in keyof T]: T[k] extends Option<infer V> ? V : never };

declare const opt1: Option<number>;
declare const opt2: Option<string>;
declare const opt3: Option<boolean>;

const zipped1 = opt1.zip1(opt2, opt3);
const zipped2 = opt1.zip2(opt2, opt3);
const zipped3 = opt1.zip3(opt2, opt3);

//// [recursiveTypeAliasWithSpreadConditionalReturnNotCircular.js]
"use strict";
exports.__esModule = true;
var zipped1 = opt1.zip1(opt2, opt3);
var zipped2 = opt1.zip2(opt2, opt3);
var zipped3 = opt1.zip3(opt2, opt3);
@@ -0,0 +1,121 @@
=== tests/cases/compiler/recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts ===
export {}
export interface Option<T> {
>Option : Symbol(Option, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 0, 9))
>T : Symbol(T, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 1, 24))

zip1<O extends Array<Option<any>>>(...others: O): Option<[T, ...UnzipOptionArray1<O>]>;
>zip1 : Symbol(Option.zip1, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 1, 28))
>O : Symbol(O, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 2, 6))
>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>Option : Symbol(Option, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 0, 9))
>others : Symbol(others, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 2, 36))
>O : Symbol(O, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 2, 6))
>Option : Symbol(Option, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 0, 9))
>T : Symbol(T, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 1, 24))
>UnzipOptionArray1 : Symbol(UnzipOptionArray1, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 9, 60))
>O : Symbol(O, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 2, 6))

zip2<O extends Array<Option<any>>>(...others: O): Option<[T, ...UnzipOptionArray2<O>]>;
>zip2 : Symbol(Option.zip2, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 2, 88))
>O : Symbol(O, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 4, 6))
>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>Option : Symbol(Option, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 0, 9))
>others : Symbol(others, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 4, 36))
>O : Symbol(O, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 4, 6))
>Option : Symbol(Option, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 0, 9))
>T : Symbol(T, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 1, 24))
>UnzipOptionArray2 : Symbol(UnzipOptionArray2, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 12, 101))
>O : Symbol(O, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 4, 6))

zip3<O extends Array<Option<any>>>(...others: O): Option<[T, ...UnzipOptionArray3<O>]>;
>zip3 : Symbol(Option.zip3, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 4, 88))
>O : Symbol(O, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 6, 6))
>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>Option : Symbol(Option, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 0, 9))
>others : Symbol(others, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 6, 36))
>O : Symbol(O, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 6, 6))
>Option : Symbol(Option, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 0, 9))
>T : Symbol(T, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 1, 24))
>UnzipOptionArray3 : Symbol(UnzipOptionArray3, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 15, 66))
>O : Symbol(O, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 6, 6))
}

type UnzipOption<T> = T extends Option<infer V> ? V : never;
>UnzipOption : Symbol(UnzipOption, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 7, 1))
>T : Symbol(T, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 9, 17))
>T : Symbol(T, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 9, 17))
>Option : Symbol(Option, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 0, 9))
>V : Symbol(V, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 9, 44))
>V : Symbol(V, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 9, 44))

/// This doesn't work
type UnzipOptionArray1<T> = { [k in keyof T]: T[k] extends Option<any> ? UnzipOption<T[k]> : never };
>UnzipOptionArray1 : Symbol(UnzipOptionArray1, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 9, 60))
>T : Symbol(T, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 12, 23))
>k : Symbol(k, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 12, 31))
>T : Symbol(T, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 12, 23))
>T : Symbol(T, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 12, 23))
>k : Symbol(k, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 12, 31))
>Option : Symbol(Option, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 0, 9))
>UnzipOption : Symbol(UnzipOption, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 7, 1))
>T : Symbol(T, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 12, 23))
>k : Symbol(k, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 12, 31))

/// But these work
type UnzipOptionArray2<T> = { [k in keyof T]: UnzipOption<T[k]> };
>UnzipOptionArray2 : Symbol(UnzipOptionArray2, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 12, 101))
>T : Symbol(T, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 15, 23))
>k : Symbol(k, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 15, 31))
>T : Symbol(T, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 15, 23))
>UnzipOption : Symbol(UnzipOption, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 7, 1))
>T : Symbol(T, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 15, 23))
>k : Symbol(k, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 15, 31))

type UnzipOptionArray3<T> = { [k in keyof T]: T[k] extends Option<infer V> ? V : never };
>UnzipOptionArray3 : Symbol(UnzipOptionArray3, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 15, 66))
>T : Symbol(T, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 16, 23))
>k : Symbol(k, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 16, 31))
>T : Symbol(T, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 16, 23))
>T : Symbol(T, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 16, 23))
>k : Symbol(k, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 16, 31))
>Option : Symbol(Option, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 0, 9))
>V : Symbol(V, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 16, 71))
>V : Symbol(V, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 16, 71))

declare const opt1: Option<number>;
>opt1 : Symbol(opt1, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 18, 13))
>Option : Symbol(Option, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 0, 9))

declare const opt2: Option<string>;
>opt2 : Symbol(opt2, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 19, 13))
>Option : Symbol(Option, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 0, 9))

declare const opt3: Option<boolean>;
>opt3 : Symbol(opt3, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 20, 13))
>Option : Symbol(Option, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 0, 9))

const zipped1 = opt1.zip1(opt2, opt3);
>zipped1 : Symbol(zipped1, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 22, 5))
>opt1.zip1 : Symbol(Option.zip1, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 1, 28))
>opt1 : Symbol(opt1, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 18, 13))
>zip1 : Symbol(Option.zip1, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 1, 28))
>opt2 : Symbol(opt2, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 19, 13))
>opt3 : Symbol(opt3, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 20, 13))

const zipped2 = opt1.zip2(opt2, opt3);
>zipped2 : Symbol(zipped2, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 23, 5))
>opt1.zip2 : Symbol(Option.zip2, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 2, 88))
>opt1 : Symbol(opt1, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 18, 13))
>zip2 : Symbol(Option.zip2, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 2, 88))
>opt2 : Symbol(opt2, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 19, 13))
>opt3 : Symbol(opt3, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 20, 13))

const zipped3 = opt1.zip3(opt2, opt3);
>zipped3 : Symbol(zipped3, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 24, 5))
>opt1.zip3 : Symbol(Option.zip3, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 4, 88))
>opt1 : Symbol(opt1, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 18, 13))
>zip3 : Symbol(Option.zip3, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 4, 88))
>opt2 : Symbol(opt2, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 19, 13))
>opt3 : Symbol(opt3, Decl(recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts, 20, 13))

@@ -0,0 +1,66 @@
=== tests/cases/compiler/recursiveTypeAliasWithSpreadConditionalReturnNotCircular.ts ===
export {}
export interface Option<T> {
zip1<O extends Array<Option<any>>>(...others: O): Option<[T, ...UnzipOptionArray1<O>]>;
>zip1 : <O extends Option<any>[]>(...others: O) => Option<[T, ...UnzipOptionArray1<O>]>
>others : O

zip2<O extends Array<Option<any>>>(...others: O): Option<[T, ...UnzipOptionArray2<O>]>;
>zip2 : <O extends Option<any>[]>(...others: O) => Option<[T, ...UnzipOptionArray2<O>]>
>others : O

zip3<O extends Array<Option<any>>>(...others: O): Option<[T, ...UnzipOptionArray3<O>]>;
>zip3 : <O extends Option<any>[]>(...others: O) => Option<[T, ...UnzipOptionArray3<O>]>
>others : O
}

type UnzipOption<T> = T extends Option<infer V> ? V : never;
>UnzipOption : UnzipOption<T>

/// This doesn't work
type UnzipOptionArray1<T> = { [k in keyof T]: T[k] extends Option<any> ? UnzipOption<T[k]> : never };
>UnzipOptionArray1 : UnzipOptionArray1<T>

/// But these work
type UnzipOptionArray2<T> = { [k in keyof T]: UnzipOption<T[k]> };
>UnzipOptionArray2 : UnzipOptionArray2<T>

type UnzipOptionArray3<T> = { [k in keyof T]: T[k] extends Option<infer V> ? V : never };
>UnzipOptionArray3 : UnzipOptionArray3<T>

declare const opt1: Option<number>;
>opt1 : Option<number>

declare const opt2: Option<string>;
>opt2 : Option<string>

declare const opt3: Option<boolean>;
>opt3 : Option<boolean>

const zipped1 = opt1.zip1(opt2, opt3);
>zipped1 : Option<[number, string, boolean]>
>opt1.zip1(opt2, opt3) : Option<[number, string, boolean]>
>opt1.zip1 : <O extends Option<any>[]>(...others: O) => Option<[number, ...UnzipOptionArray1<O>]>
>opt1 : Option<number>
>zip1 : <O extends Option<any>[]>(...others: O) => Option<[number, ...UnzipOptionArray1<O>]>
>opt2 : Option<string>
>opt3 : Option<boolean>

const zipped2 = opt1.zip2(opt2, opt3);
>zipped2 : Option<[number, string, boolean]>
>opt1.zip2(opt2, opt3) : Option<[number, string, boolean]>
>opt1.zip2 : <O extends Option<any>[]>(...others: O) => Option<[number, ...UnzipOptionArray2<O>]>
>opt1 : Option<number>
>zip2 : <O extends Option<any>[]>(...others: O) => Option<[number, ...UnzipOptionArray2<O>]>
>opt2 : Option<string>
>opt3 : Option<boolean>

const zipped3 = opt1.zip3(opt2, opt3);
>zipped3 : Option<[number, string, boolean]>
>opt1.zip3(opt2, opt3) : Option<[number, string, boolean]>
>opt1.zip3 : <O extends Option<any>[]>(...others: O) => Option<[number, ...UnzipOptionArray3<O>]>
>opt1 : Option<number>
>zip3 : <O extends Option<any>[]>(...others: O) => Option<[number, ...UnzipOptionArray3<O>]>
>opt2 : Option<string>
>opt3 : Option<boolean>

@@ -0,0 +1,25 @@
export {}
export interface Option<T> {
zip1<O extends Array<Option<any>>>(...others: O): Option<[T, ...UnzipOptionArray1<O>]>;

zip2<O extends Array<Option<any>>>(...others: O): Option<[T, ...UnzipOptionArray2<O>]>;

zip3<O extends Array<Option<any>>>(...others: O): Option<[T, ...UnzipOptionArray3<O>]>;
}

type UnzipOption<T> = T extends Option<infer V> ? V : never;

/// This doesn't work
type UnzipOptionArray1<T> = { [k in keyof T]: T[k] extends Option<any> ? UnzipOption<T[k]> : never };

/// But these work
type UnzipOptionArray2<T> = { [k in keyof T]: UnzipOption<T[k]> };
type UnzipOptionArray3<T> = { [k in keyof T]: T[k] extends Option<infer V> ? V : never };

declare const opt1: Option<number>;
declare const opt2: Option<string>;
declare const opt3: Option<boolean>;

const zipped1 = opt1.zip1(opt2, opt3);
const zipped2 = opt1.zip2(opt2, opt3);
const zipped3 = opt1.zip3(opt2, opt3);

0 comments on commit bdcc240

Please sign in to comment.