From 4fa96056eaf038fa0796c2acb6631f3344603a1c Mon Sep 17 00:00:00 2001 From: Kevin Donnelly Date: Tue, 27 Mar 2018 09:45:25 -0500 Subject: [PATCH] Make T[never] = never instead of erroring or being any (#22787) * Add tests showing existing behavior for indexing types with never. * Make T[never] = never instead of erroring or being any. And update the baselines for the tests for this change. * Add test case for indexing an expression with never showing existing behavior. * Make indexing an object with never expression result in never. And update baseline to reflect new behavior. --- src/compiler/checker.ts | 3 + .../reference/indexingTypesWithNever.js | 121 ++++++ .../reference/indexingTypesWithNever.symbols | 389 +++++++++++++++++ .../reference/indexingTypesWithNever.types | 404 ++++++++++++++++++ .../cases/compiler/indexingTypesWithNever.ts | 111 +++++ 5 files changed, 1028 insertions(+) create mode 100644 tests/baselines/reference/indexingTypesWithNever.js create mode 100644 tests/baselines/reference/indexingTypesWithNever.symbols create mode 100644 tests/baselines/reference/indexingTypesWithNever.types create mode 100644 tests/cases/compiler/indexingTypesWithNever.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5bf1f022869ee..cc17e30e0e098 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8151,6 +8151,9 @@ namespace ts { } return indexInfo.type; } + if (indexType.flags & TypeFlags.Never) { + return neverType; + } if (accessExpression && !isConstEnumObjectType(objectType)) { if (noImplicitAny && !compilerOptions.suppressImplicitAnyIndexErrors) { if (getIndexTypeOfType(objectType, IndexKind.Number)) { diff --git a/tests/baselines/reference/indexingTypesWithNever.js b/tests/baselines/reference/indexingTypesWithNever.js new file mode 100644 index 0000000000000..4e174fc4c9d91 --- /dev/null +++ b/tests/baselines/reference/indexingTypesWithNever.js @@ -0,0 +1,121 @@ +//// [indexingTypesWithNever.ts] +type TestObj = { + a: string; + b: number; +}; + +// Should be never but without an error +type Result1 = TestObj[never]; + +type EmptyObj = {}; + +// Should be never but without an error +type Result2 = EmptyObj[keyof EmptyObj]; + +declare function genericFn1(obj: T): T[never]; + +// Should be never +const result3 = genericFn1({ c: "ctest", d: "dtest" }); + +declare function genericFn2( + obj: T +): T[never]; + +// Should be never +const result4 = genericFn2({ e: "etest", f: "ftest" }); + +declare function genericFn3< + T extends { [K in keyof T]: T[K] }, + U extends keyof T, + V extends keyof T +>(obj: T, u: U, v: V): T[U & V]; + +// Should be never +const result5 = genericFn3({ g: "gtest", h: "htest" }, "g", "h"); // 'g' & 'h' will reduce to never + + +declare const obj: {a: string, b: number} +declare const key: never + +const result6 = obj[key] + +// Expanded examples from https://github.com/Microsoft/TypeScript/issues/21988 +type RequiredPropNames = { + [P in keyof T]-?: undefined extends T[P] ? never : P +}[keyof T]; + +type OptionalPropNames = { + [P in keyof T]-?: undefined extends T[P] ? P : never +}[keyof T]; + +type RequiredProps = { [P in RequiredPropNames]: T[P] }; +type OptionalProps = { [P in OptionalPropNames]?: T[P] }; + +type Match = [Exp] extends [Act] + ? ([Act] extends [Exp] ? "Match" : "Did not match 2") + : "Did not match 1"; + +type ExpectType = Match extends "Match" + ? ({} extends Exp ? Match, Required> : "Match") + : "Did not match"; + +type P3 = { a: string; b: number; c?: boolean }; +type P2 = { a: string; c?: boolean }; +type P1 = { c?: boolean }; +type P0 = {}; + +type P3Names = RequiredPropNames; // expect 'a' | 'b' +type P2Names = RequiredPropNames; // expect 'a' +type P1Names = RequiredPropNames; // expect never +type P0Names = RequiredPropNames; // expect never + +declare const p3NameTest: ExpectType<"a" | "b", P3Names>; +declare const p2NameTest: ExpectType<"a", P2Names>; +declare const p1NameTest: ExpectType; +declare const p0NameTest: ExpectType; + +type P3Props = RequiredProps; // expect { a: string; b: number } +type P2Props = RequiredProps; // expect { a: string; } +type P1Props = RequiredProps; // expect {} +type P0Props = RequiredProps; // expect {} + +declare const p3Test: ExpectType<{ a: string; b: number }, P3Props>; +declare const p2Test: ExpectType<{ a: string }, P2Props>; +declare const p1Test: ExpectType<{}, P1Props>; +declare const p0Test: ExpectType<{}, P0Props>; + +type O3 = { a?: string; b?: number; c: boolean }; +type O2 = { a?: string; c: boolean }; +type O1 = { c: boolean }; +type O0 = {}; + +type O3Names = OptionalPropNames; // expect 'a' | 'b' +type O2Names = OptionalPropNames; // expect 'a' +type O1Names = OptionalPropNames; // expect never +type O0Names = OptionalPropNames; // expect never + +declare const o3NameTest: ExpectType<"a" | "b", O3Names>; +declare const o2NameTest: ExpectType<"a", O2Names>; +declare const o1NameTest: ExpectType; +declare const o0NameTest: ExpectType; + +type O3Props = OptionalProps; // expect { a?: string | undefined; b?: number | undefined } +type O2Props = OptionalProps; // expect { a?: string | undefined; } +type O1Props = OptionalProps; // expect {} +type O0Props = OptionalProps; // expect {} + +declare const o3Test: ExpectType<{ a?: string; b?: number }, O3Props>; +declare const o2Test: ExpectType<{ a?: string }, O2Props>; +declare const o1Test: ExpectType<{}, O1Props>; +declare const o0Test: ExpectType<{}, O0Props>; + + +//// [indexingTypesWithNever.js] +"use strict"; +// Should be never +var result3 = genericFn1({ c: "ctest", d: "dtest" }); +// Should be never +var result4 = genericFn2({ e: "etest", f: "ftest" }); +// Should be never +var result5 = genericFn3({ g: "gtest", h: "htest" }, "g", "h"); // 'g' & 'h' will reduce to never +var result6 = obj[key]; diff --git a/tests/baselines/reference/indexingTypesWithNever.symbols b/tests/baselines/reference/indexingTypesWithNever.symbols new file mode 100644 index 0000000000000..2b163d74a0260 --- /dev/null +++ b/tests/baselines/reference/indexingTypesWithNever.symbols @@ -0,0 +1,389 @@ +=== tests/cases/compiler/indexingTypesWithNever.ts === +type TestObj = { +>TestObj : Symbol(TestObj, Decl(indexingTypesWithNever.ts, 0, 0)) + + a: string; +>a : Symbol(a, Decl(indexingTypesWithNever.ts, 0, 16)) + + b: number; +>b : Symbol(b, Decl(indexingTypesWithNever.ts, 1, 12)) + +}; + +// Should be never but without an error +type Result1 = TestObj[never]; +>Result1 : Symbol(Result1, Decl(indexingTypesWithNever.ts, 3, 2)) +>TestObj : Symbol(TestObj, Decl(indexingTypesWithNever.ts, 0, 0)) + +type EmptyObj = {}; +>EmptyObj : Symbol(EmptyObj, Decl(indexingTypesWithNever.ts, 6, 30)) + +// Should be never but without an error +type Result2 = EmptyObj[keyof EmptyObj]; +>Result2 : Symbol(Result2, Decl(indexingTypesWithNever.ts, 8, 19)) +>EmptyObj : Symbol(EmptyObj, Decl(indexingTypesWithNever.ts, 6, 30)) +>EmptyObj : Symbol(EmptyObj, Decl(indexingTypesWithNever.ts, 6, 30)) + +declare function genericFn1(obj: T): T[never]; +>genericFn1 : Symbol(genericFn1, Decl(indexingTypesWithNever.ts, 11, 40)) +>T : Symbol(T, Decl(indexingTypesWithNever.ts, 13, 28)) +>obj : Symbol(obj, Decl(indexingTypesWithNever.ts, 13, 31)) +>T : Symbol(T, Decl(indexingTypesWithNever.ts, 13, 28)) +>T : Symbol(T, Decl(indexingTypesWithNever.ts, 13, 28)) + +// Should be never +const result3 = genericFn1({ c: "ctest", d: "dtest" }); +>result3 : Symbol(result3, Decl(indexingTypesWithNever.ts, 16, 5)) +>genericFn1 : Symbol(genericFn1, Decl(indexingTypesWithNever.ts, 11, 40)) +>c : Symbol(c, Decl(indexingTypesWithNever.ts, 16, 28)) +>d : Symbol(d, Decl(indexingTypesWithNever.ts, 16, 40)) + +declare function genericFn2( +>genericFn2 : Symbol(genericFn2, Decl(indexingTypesWithNever.ts, 16, 55)) +>T : Symbol(T, Decl(indexingTypesWithNever.ts, 18, 28)) +>ind : Symbol(ind, Decl(indexingTypesWithNever.ts, 18, 41)) + + obj: T +>obj : Symbol(obj, Decl(indexingTypesWithNever.ts, 18, 65)) +>T : Symbol(T, Decl(indexingTypesWithNever.ts, 18, 28)) + +): T[never]; +>T : Symbol(T, Decl(indexingTypesWithNever.ts, 18, 28)) + +// Should be never +const result4 = genericFn2({ e: "etest", f: "ftest" }); +>result4 : Symbol(result4, Decl(indexingTypesWithNever.ts, 23, 5)) +>genericFn2 : Symbol(genericFn2, Decl(indexingTypesWithNever.ts, 16, 55)) +>e : Symbol(e, Decl(indexingTypesWithNever.ts, 23, 28)) +>f : Symbol(f, Decl(indexingTypesWithNever.ts, 23, 40)) + +declare function genericFn3< +>genericFn3 : Symbol(genericFn3, Decl(indexingTypesWithNever.ts, 23, 55)) + + T extends { [K in keyof T]: T[K] }, +>T : Symbol(T, Decl(indexingTypesWithNever.ts, 25, 28)) +>K : Symbol(K, Decl(indexingTypesWithNever.ts, 26, 15)) +>T : Symbol(T, Decl(indexingTypesWithNever.ts, 25, 28)) +>T : Symbol(T, Decl(indexingTypesWithNever.ts, 25, 28)) +>K : Symbol(K, Decl(indexingTypesWithNever.ts, 26, 15)) + + U extends keyof T, +>U : Symbol(U, Decl(indexingTypesWithNever.ts, 26, 37)) +>T : Symbol(T, Decl(indexingTypesWithNever.ts, 25, 28)) + + V extends keyof T +>V : Symbol(V, Decl(indexingTypesWithNever.ts, 27, 20)) +>T : Symbol(T, Decl(indexingTypesWithNever.ts, 25, 28)) + +>(obj: T, u: U, v: V): T[U & V]; +>obj : Symbol(obj, Decl(indexingTypesWithNever.ts, 29, 2)) +>T : Symbol(T, Decl(indexingTypesWithNever.ts, 25, 28)) +>u : Symbol(u, Decl(indexingTypesWithNever.ts, 29, 9)) +>U : Symbol(U, Decl(indexingTypesWithNever.ts, 26, 37)) +>v : Symbol(v, Decl(indexingTypesWithNever.ts, 29, 15)) +>V : Symbol(V, Decl(indexingTypesWithNever.ts, 27, 20)) +>T : Symbol(T, Decl(indexingTypesWithNever.ts, 25, 28)) +>U : Symbol(U, Decl(indexingTypesWithNever.ts, 26, 37)) +>V : Symbol(V, Decl(indexingTypesWithNever.ts, 27, 20)) + +// Should be never +const result5 = genericFn3({ g: "gtest", h: "htest" }, "g", "h"); // 'g' & 'h' will reduce to never +>result5 : Symbol(result5, Decl(indexingTypesWithNever.ts, 32, 5)) +>genericFn3 : Symbol(genericFn3, Decl(indexingTypesWithNever.ts, 23, 55)) +>g : Symbol(g, Decl(indexingTypesWithNever.ts, 32, 28)) +>h : Symbol(h, Decl(indexingTypesWithNever.ts, 32, 40)) + + +declare const obj: {a: string, b: number} +>obj : Symbol(obj, Decl(indexingTypesWithNever.ts, 35, 13)) +>a : Symbol(a, Decl(indexingTypesWithNever.ts, 35, 20)) +>b : Symbol(b, Decl(indexingTypesWithNever.ts, 35, 30)) + +declare const key: never +>key : Symbol(key, Decl(indexingTypesWithNever.ts, 36, 13)) + +const result6 = obj[key] +>result6 : Symbol(result6, Decl(indexingTypesWithNever.ts, 38, 5)) +>obj : Symbol(obj, Decl(indexingTypesWithNever.ts, 35, 13)) +>key : Symbol(key, Decl(indexingTypesWithNever.ts, 36, 13)) + +// Expanded examples from https://github.com/Microsoft/TypeScript/issues/21988 +type RequiredPropNames = { +>RequiredPropNames : Symbol(RequiredPropNames, Decl(indexingTypesWithNever.ts, 38, 24)) +>T : Symbol(T, Decl(indexingTypesWithNever.ts, 41, 23)) + + [P in keyof T]-?: undefined extends T[P] ? never : P +>P : Symbol(P, Decl(indexingTypesWithNever.ts, 42, 3)) +>T : Symbol(T, Decl(indexingTypesWithNever.ts, 41, 23)) +>T : Symbol(T, Decl(indexingTypesWithNever.ts, 41, 23)) +>P : Symbol(P, Decl(indexingTypesWithNever.ts, 42, 3)) +>P : Symbol(P, Decl(indexingTypesWithNever.ts, 42, 3)) + +}[keyof T]; +>T : Symbol(T, Decl(indexingTypesWithNever.ts, 41, 23)) + +type OptionalPropNames = { +>OptionalPropNames : Symbol(OptionalPropNames, Decl(indexingTypesWithNever.ts, 43, 11)) +>T : Symbol(T, Decl(indexingTypesWithNever.ts, 45, 23)) + + [P in keyof T]-?: undefined extends T[P] ? P : never +>P : Symbol(P, Decl(indexingTypesWithNever.ts, 46, 3)) +>T : Symbol(T, Decl(indexingTypesWithNever.ts, 45, 23)) +>T : Symbol(T, Decl(indexingTypesWithNever.ts, 45, 23)) +>P : Symbol(P, Decl(indexingTypesWithNever.ts, 46, 3)) +>P : Symbol(P, Decl(indexingTypesWithNever.ts, 46, 3)) + +}[keyof T]; +>T : Symbol(T, Decl(indexingTypesWithNever.ts, 45, 23)) + +type RequiredProps = { [P in RequiredPropNames]: T[P] }; +>RequiredProps : Symbol(RequiredProps, Decl(indexingTypesWithNever.ts, 47, 11)) +>T : Symbol(T, Decl(indexingTypesWithNever.ts, 49, 19)) +>P : Symbol(P, Decl(indexingTypesWithNever.ts, 49, 27)) +>RequiredPropNames : Symbol(RequiredPropNames, Decl(indexingTypesWithNever.ts, 38, 24)) +>T : Symbol(T, Decl(indexingTypesWithNever.ts, 49, 19)) +>T : Symbol(T, Decl(indexingTypesWithNever.ts, 49, 19)) +>P : Symbol(P, Decl(indexingTypesWithNever.ts, 49, 27)) + +type OptionalProps = { [P in OptionalPropNames]?: T[P] }; +>OptionalProps : Symbol(OptionalProps, Decl(indexingTypesWithNever.ts, 49, 62)) +>T : Symbol(T, Decl(indexingTypesWithNever.ts, 50, 19)) +>P : Symbol(P, Decl(indexingTypesWithNever.ts, 50, 27)) +>OptionalPropNames : Symbol(OptionalPropNames, Decl(indexingTypesWithNever.ts, 43, 11)) +>T : Symbol(T, Decl(indexingTypesWithNever.ts, 50, 19)) +>T : Symbol(T, Decl(indexingTypesWithNever.ts, 50, 19)) +>P : Symbol(P, Decl(indexingTypesWithNever.ts, 50, 27)) + +type Match = [Exp] extends [Act] +>Match : Symbol(Match, Decl(indexingTypesWithNever.ts, 50, 63)) +>Exp : Symbol(Exp, Decl(indexingTypesWithNever.ts, 52, 11)) +>Act : Symbol(Act, Decl(indexingTypesWithNever.ts, 52, 15)) +>Exp : Symbol(Exp, Decl(indexingTypesWithNever.ts, 52, 11)) +>Act : Symbol(Act, Decl(indexingTypesWithNever.ts, 52, 15)) + + ? ([Act] extends [Exp] ? "Match" : "Did not match 2") +>Act : Symbol(Act, Decl(indexingTypesWithNever.ts, 52, 15)) +>Exp : Symbol(Exp, Decl(indexingTypesWithNever.ts, 52, 11)) + + : "Did not match 1"; + +type ExpectType = Match extends "Match" +>ExpectType : Symbol(ExpectType, Decl(indexingTypesWithNever.ts, 54, 22)) +>Exp : Symbol(Exp, Decl(indexingTypesWithNever.ts, 56, 16)) +>Act : Symbol(Act, Decl(indexingTypesWithNever.ts, 56, 20)) +>Match : Symbol(Match, Decl(indexingTypesWithNever.ts, 50, 63)) +>Exp : Symbol(Exp, Decl(indexingTypesWithNever.ts, 56, 16)) +>Act : Symbol(Act, Decl(indexingTypesWithNever.ts, 56, 20)) + + ? ({} extends Exp ? Match, Required> : "Match") +>Exp : Symbol(Exp, Decl(indexingTypesWithNever.ts, 56, 16)) +>Match : Symbol(Match, Decl(indexingTypesWithNever.ts, 50, 63)) +>Required : Symbol(Required, Decl(lib.d.ts, --, --)) +>Exp : Symbol(Exp, Decl(indexingTypesWithNever.ts, 56, 16)) +>Required : Symbol(Required, Decl(lib.d.ts, --, --)) +>Act : Symbol(Act, Decl(indexingTypesWithNever.ts, 56, 20)) + + : "Did not match"; + +type P3 = { a: string; b: number; c?: boolean }; +>P3 : Symbol(P3, Decl(indexingTypesWithNever.ts, 58, 20)) +>a : Symbol(a, Decl(indexingTypesWithNever.ts, 60, 11)) +>b : Symbol(b, Decl(indexingTypesWithNever.ts, 60, 22)) +>c : Symbol(c, Decl(indexingTypesWithNever.ts, 60, 33)) + +type P2 = { a: string; c?: boolean }; +>P2 : Symbol(P2, Decl(indexingTypesWithNever.ts, 60, 48)) +>a : Symbol(a, Decl(indexingTypesWithNever.ts, 61, 11)) +>c : Symbol(c, Decl(indexingTypesWithNever.ts, 61, 22)) + +type P1 = { c?: boolean }; +>P1 : Symbol(P1, Decl(indexingTypesWithNever.ts, 61, 37)) +>c : Symbol(c, Decl(indexingTypesWithNever.ts, 62, 11)) + +type P0 = {}; +>P0 : Symbol(P0, Decl(indexingTypesWithNever.ts, 62, 26)) + +type P3Names = RequiredPropNames; // expect 'a' | 'b' +>P3Names : Symbol(P3Names, Decl(indexingTypesWithNever.ts, 63, 13)) +>RequiredPropNames : Symbol(RequiredPropNames, Decl(indexingTypesWithNever.ts, 38, 24)) +>P3 : Symbol(P3, Decl(indexingTypesWithNever.ts, 58, 20)) + +type P2Names = RequiredPropNames; // expect 'a' +>P2Names : Symbol(P2Names, Decl(indexingTypesWithNever.ts, 65, 37)) +>RequiredPropNames : Symbol(RequiredPropNames, Decl(indexingTypesWithNever.ts, 38, 24)) +>P2 : Symbol(P2, Decl(indexingTypesWithNever.ts, 60, 48)) + +type P1Names = RequiredPropNames; // expect never +>P1Names : Symbol(P1Names, Decl(indexingTypesWithNever.ts, 66, 37)) +>RequiredPropNames : Symbol(RequiredPropNames, Decl(indexingTypesWithNever.ts, 38, 24)) +>P1 : Symbol(P1, Decl(indexingTypesWithNever.ts, 61, 37)) + +type P0Names = RequiredPropNames; // expect never +>P0Names : Symbol(P0Names, Decl(indexingTypesWithNever.ts, 67, 37)) +>RequiredPropNames : Symbol(RequiredPropNames, Decl(indexingTypesWithNever.ts, 38, 24)) +>P0 : Symbol(P0, Decl(indexingTypesWithNever.ts, 62, 26)) + +declare const p3NameTest: ExpectType<"a" | "b", P3Names>; +>p3NameTest : Symbol(p3NameTest, Decl(indexingTypesWithNever.ts, 70, 13)) +>ExpectType : Symbol(ExpectType, Decl(indexingTypesWithNever.ts, 54, 22)) +>P3Names : Symbol(P3Names, Decl(indexingTypesWithNever.ts, 63, 13)) + +declare const p2NameTest: ExpectType<"a", P2Names>; +>p2NameTest : Symbol(p2NameTest, Decl(indexingTypesWithNever.ts, 71, 13)) +>ExpectType : Symbol(ExpectType, Decl(indexingTypesWithNever.ts, 54, 22)) +>P2Names : Symbol(P2Names, Decl(indexingTypesWithNever.ts, 65, 37)) + +declare const p1NameTest: ExpectType; +>p1NameTest : Symbol(p1NameTest, Decl(indexingTypesWithNever.ts, 72, 13)) +>ExpectType : Symbol(ExpectType, Decl(indexingTypesWithNever.ts, 54, 22)) +>P1Names : Symbol(P1Names, Decl(indexingTypesWithNever.ts, 66, 37)) + +declare const p0NameTest: ExpectType; +>p0NameTest : Symbol(p0NameTest, Decl(indexingTypesWithNever.ts, 73, 13)) +>ExpectType : Symbol(ExpectType, Decl(indexingTypesWithNever.ts, 54, 22)) +>P0Names : Symbol(P0Names, Decl(indexingTypesWithNever.ts, 67, 37)) + +type P3Props = RequiredProps; // expect { a: string; b: number } +>P3Props : Symbol(P3Props, Decl(indexingTypesWithNever.ts, 73, 53)) +>RequiredProps : Symbol(RequiredProps, Decl(indexingTypesWithNever.ts, 47, 11)) +>P3 : Symbol(P3, Decl(indexingTypesWithNever.ts, 58, 20)) + +type P2Props = RequiredProps; // expect { a: string; } +>P2Props : Symbol(P2Props, Decl(indexingTypesWithNever.ts, 75, 33)) +>RequiredProps : Symbol(RequiredProps, Decl(indexingTypesWithNever.ts, 47, 11)) +>P2 : Symbol(P2, Decl(indexingTypesWithNever.ts, 60, 48)) + +type P1Props = RequiredProps; // expect {} +>P1Props : Symbol(P1Props, Decl(indexingTypesWithNever.ts, 76, 33)) +>RequiredProps : Symbol(RequiredProps, Decl(indexingTypesWithNever.ts, 47, 11)) +>P1 : Symbol(P1, Decl(indexingTypesWithNever.ts, 61, 37)) + +type P0Props = RequiredProps; // expect {} +>P0Props : Symbol(P0Props, Decl(indexingTypesWithNever.ts, 77, 33)) +>RequiredProps : Symbol(RequiredProps, Decl(indexingTypesWithNever.ts, 47, 11)) +>P0 : Symbol(P0, Decl(indexingTypesWithNever.ts, 62, 26)) + +declare const p3Test: ExpectType<{ a: string; b: number }, P3Props>; +>p3Test : Symbol(p3Test, Decl(indexingTypesWithNever.ts, 80, 13)) +>ExpectType : Symbol(ExpectType, Decl(indexingTypesWithNever.ts, 54, 22)) +>a : Symbol(a, Decl(indexingTypesWithNever.ts, 80, 34)) +>b : Symbol(b, Decl(indexingTypesWithNever.ts, 80, 45)) +>P3Props : Symbol(P3Props, Decl(indexingTypesWithNever.ts, 73, 53)) + +declare const p2Test: ExpectType<{ a: string }, P2Props>; +>p2Test : Symbol(p2Test, Decl(indexingTypesWithNever.ts, 81, 13)) +>ExpectType : Symbol(ExpectType, Decl(indexingTypesWithNever.ts, 54, 22)) +>a : Symbol(a, Decl(indexingTypesWithNever.ts, 81, 34)) +>P2Props : Symbol(P2Props, Decl(indexingTypesWithNever.ts, 75, 33)) + +declare const p1Test: ExpectType<{}, P1Props>; +>p1Test : Symbol(p1Test, Decl(indexingTypesWithNever.ts, 82, 13)) +>ExpectType : Symbol(ExpectType, Decl(indexingTypesWithNever.ts, 54, 22)) +>P1Props : Symbol(P1Props, Decl(indexingTypesWithNever.ts, 76, 33)) + +declare const p0Test: ExpectType<{}, P0Props>; +>p0Test : Symbol(p0Test, Decl(indexingTypesWithNever.ts, 83, 13)) +>ExpectType : Symbol(ExpectType, Decl(indexingTypesWithNever.ts, 54, 22)) +>P0Props : Symbol(P0Props, Decl(indexingTypesWithNever.ts, 77, 33)) + +type O3 = { a?: string; b?: number; c: boolean }; +>O3 : Symbol(O3, Decl(indexingTypesWithNever.ts, 83, 46)) +>a : Symbol(a, Decl(indexingTypesWithNever.ts, 85, 11)) +>b : Symbol(b, Decl(indexingTypesWithNever.ts, 85, 23)) +>c : Symbol(c, Decl(indexingTypesWithNever.ts, 85, 35)) + +type O2 = { a?: string; c: boolean }; +>O2 : Symbol(O2, Decl(indexingTypesWithNever.ts, 85, 49)) +>a : Symbol(a, Decl(indexingTypesWithNever.ts, 86, 11)) +>c : Symbol(c, Decl(indexingTypesWithNever.ts, 86, 23)) + +type O1 = { c: boolean }; +>O1 : Symbol(O1, Decl(indexingTypesWithNever.ts, 86, 37)) +>c : Symbol(c, Decl(indexingTypesWithNever.ts, 87, 11)) + +type O0 = {}; +>O0 : Symbol(O0, Decl(indexingTypesWithNever.ts, 87, 25)) + +type O3Names = OptionalPropNames; // expect 'a' | 'b' +>O3Names : Symbol(O3Names, Decl(indexingTypesWithNever.ts, 88, 13)) +>OptionalPropNames : Symbol(OptionalPropNames, Decl(indexingTypesWithNever.ts, 43, 11)) +>O3 : Symbol(O3, Decl(indexingTypesWithNever.ts, 83, 46)) + +type O2Names = OptionalPropNames; // expect 'a' +>O2Names : Symbol(O2Names, Decl(indexingTypesWithNever.ts, 90, 37)) +>OptionalPropNames : Symbol(OptionalPropNames, Decl(indexingTypesWithNever.ts, 43, 11)) +>O2 : Symbol(O2, Decl(indexingTypesWithNever.ts, 85, 49)) + +type O1Names = OptionalPropNames; // expect never +>O1Names : Symbol(O1Names, Decl(indexingTypesWithNever.ts, 91, 37)) +>OptionalPropNames : Symbol(OptionalPropNames, Decl(indexingTypesWithNever.ts, 43, 11)) +>O1 : Symbol(O1, Decl(indexingTypesWithNever.ts, 86, 37)) + +type O0Names = OptionalPropNames; // expect never +>O0Names : Symbol(O0Names, Decl(indexingTypesWithNever.ts, 92, 37)) +>OptionalPropNames : Symbol(OptionalPropNames, Decl(indexingTypesWithNever.ts, 43, 11)) +>O0 : Symbol(O0, Decl(indexingTypesWithNever.ts, 87, 25)) + +declare const o3NameTest: ExpectType<"a" | "b", O3Names>; +>o3NameTest : Symbol(o3NameTest, Decl(indexingTypesWithNever.ts, 95, 13)) +>ExpectType : Symbol(ExpectType, Decl(indexingTypesWithNever.ts, 54, 22)) +>O3Names : Symbol(O3Names, Decl(indexingTypesWithNever.ts, 88, 13)) + +declare const o2NameTest: ExpectType<"a", O2Names>; +>o2NameTest : Symbol(o2NameTest, Decl(indexingTypesWithNever.ts, 96, 13)) +>ExpectType : Symbol(ExpectType, Decl(indexingTypesWithNever.ts, 54, 22)) +>O2Names : Symbol(O2Names, Decl(indexingTypesWithNever.ts, 90, 37)) + +declare const o1NameTest: ExpectType; +>o1NameTest : Symbol(o1NameTest, Decl(indexingTypesWithNever.ts, 97, 13)) +>ExpectType : Symbol(ExpectType, Decl(indexingTypesWithNever.ts, 54, 22)) +>O1Names : Symbol(O1Names, Decl(indexingTypesWithNever.ts, 91, 37)) + +declare const o0NameTest: ExpectType; +>o0NameTest : Symbol(o0NameTest, Decl(indexingTypesWithNever.ts, 98, 13)) +>ExpectType : Symbol(ExpectType, Decl(indexingTypesWithNever.ts, 54, 22)) +>O0Names : Symbol(O0Names, Decl(indexingTypesWithNever.ts, 92, 37)) + +type O3Props = OptionalProps; // expect { a?: string | undefined; b?: number | undefined } +>O3Props : Symbol(O3Props, Decl(indexingTypesWithNever.ts, 98, 53)) +>OptionalProps : Symbol(OptionalProps, Decl(indexingTypesWithNever.ts, 49, 62)) +>O3 : Symbol(O3, Decl(indexingTypesWithNever.ts, 83, 46)) + +type O2Props = OptionalProps; // expect { a?: string | undefined; } +>O2Props : Symbol(O2Props, Decl(indexingTypesWithNever.ts, 100, 33)) +>OptionalProps : Symbol(OptionalProps, Decl(indexingTypesWithNever.ts, 49, 62)) +>O2 : Symbol(O2, Decl(indexingTypesWithNever.ts, 85, 49)) + +type O1Props = OptionalProps; // expect {} +>O1Props : Symbol(O1Props, Decl(indexingTypesWithNever.ts, 101, 33)) +>OptionalProps : Symbol(OptionalProps, Decl(indexingTypesWithNever.ts, 49, 62)) +>O1 : Symbol(O1, Decl(indexingTypesWithNever.ts, 86, 37)) + +type O0Props = OptionalProps; // expect {} +>O0Props : Symbol(O0Props, Decl(indexingTypesWithNever.ts, 102, 33)) +>OptionalProps : Symbol(OptionalProps, Decl(indexingTypesWithNever.ts, 49, 62)) +>O0 : Symbol(O0, Decl(indexingTypesWithNever.ts, 87, 25)) + +declare const o3Test: ExpectType<{ a?: string; b?: number }, O3Props>; +>o3Test : Symbol(o3Test, Decl(indexingTypesWithNever.ts, 105, 13)) +>ExpectType : Symbol(ExpectType, Decl(indexingTypesWithNever.ts, 54, 22)) +>a : Symbol(a, Decl(indexingTypesWithNever.ts, 105, 34)) +>b : Symbol(b, Decl(indexingTypesWithNever.ts, 105, 46)) +>O3Props : Symbol(O3Props, Decl(indexingTypesWithNever.ts, 98, 53)) + +declare const o2Test: ExpectType<{ a?: string }, O2Props>; +>o2Test : Symbol(o2Test, Decl(indexingTypesWithNever.ts, 106, 13)) +>ExpectType : Symbol(ExpectType, Decl(indexingTypesWithNever.ts, 54, 22)) +>a : Symbol(a, Decl(indexingTypesWithNever.ts, 106, 34)) +>O2Props : Symbol(O2Props, Decl(indexingTypesWithNever.ts, 100, 33)) + +declare const o1Test: ExpectType<{}, O1Props>; +>o1Test : Symbol(o1Test, Decl(indexingTypesWithNever.ts, 107, 13)) +>ExpectType : Symbol(ExpectType, Decl(indexingTypesWithNever.ts, 54, 22)) +>O1Props : Symbol(O1Props, Decl(indexingTypesWithNever.ts, 101, 33)) + +declare const o0Test: ExpectType<{}, O0Props>; +>o0Test : Symbol(o0Test, Decl(indexingTypesWithNever.ts, 108, 13)) +>ExpectType : Symbol(ExpectType, Decl(indexingTypesWithNever.ts, 54, 22)) +>O0Props : Symbol(O0Props, Decl(indexingTypesWithNever.ts, 102, 33)) + diff --git a/tests/baselines/reference/indexingTypesWithNever.types b/tests/baselines/reference/indexingTypesWithNever.types new file mode 100644 index 0000000000000..fb6b3904f3a5a --- /dev/null +++ b/tests/baselines/reference/indexingTypesWithNever.types @@ -0,0 +1,404 @@ +=== tests/cases/compiler/indexingTypesWithNever.ts === +type TestObj = { +>TestObj : TestObj + + a: string; +>a : string + + b: number; +>b : number + +}; + +// Should be never but without an error +type Result1 = TestObj[never]; +>Result1 : never +>TestObj : TestObj + +type EmptyObj = {}; +>EmptyObj : EmptyObj + +// Should be never but without an error +type Result2 = EmptyObj[keyof EmptyObj]; +>Result2 : never +>EmptyObj : EmptyObj +>EmptyObj : EmptyObj + +declare function genericFn1(obj: T): T[never]; +>genericFn1 : (obj: T) => T[never] +>T : T +>obj : T +>T : T +>T : T + +// Should be never +const result3 = genericFn1({ c: "ctest", d: "dtest" }); +>result3 : never +>genericFn1({ c: "ctest", d: "dtest" }) : never +>genericFn1 : (obj: T) => T[never] +>{ c: "ctest", d: "dtest" } : { c: string; d: string; } +>c : string +>"ctest" : "ctest" +>d : string +>"dtest" : "dtest" + +declare function genericFn2( +>genericFn2 : (obj: T) => T[never] +>T : T +>ind : string + + obj: T +>obj : T +>T : T + +): T[never]; +>T : T + +// Should be never +const result4 = genericFn2({ e: "etest", f: "ftest" }); +>result4 : never +>genericFn2({ e: "etest", f: "ftest" }) : never +>genericFn2 : (obj: T) => T[never] +>{ e: "etest", f: "ftest" } : { e: string; f: string; } +>e : string +>"etest" : "etest" +>f : string +>"ftest" : "ftest" + +declare function genericFn3< +>genericFn3 : (obj: T, u: U, v: V) => T[U & V] + + T extends { [K in keyof T]: T[K] }, +>T : T +>K : K +>T : T +>T : T +>K : K + + U extends keyof T, +>U : U +>T : T + + V extends keyof T +>V : V +>T : T + +>(obj: T, u: U, v: V): T[U & V]; +>obj : T +>T : T +>u : U +>U : U +>v : V +>V : V +>T : T +>U : U +>V : V + +// Should be never +const result5 = genericFn3({ g: "gtest", h: "htest" }, "g", "h"); // 'g' & 'h' will reduce to never +>result5 : any +>genericFn3({ g: "gtest", h: "htest" }, "g", "h") : any +>genericFn3 : (obj: T, u: U, v: V) => T[U & V] +>{ g: "gtest", h: "htest" } : { g: string; h: string; } +>g : string +>"gtest" : "gtest" +>h : string +>"htest" : "htest" +>"g" : "g" +>"h" : "h" + + +declare const obj: {a: string, b: number} +>obj : { a: string; b: number; } +>a : string +>b : number + +declare const key: never +>key : never + +const result6 = obj[key] +>result6 : never +>obj[key] : never +>obj : { a: string; b: number; } +>key : never + +// Expanded examples from https://github.com/Microsoft/TypeScript/issues/21988 +type RequiredPropNames = { +>RequiredPropNames : { [P in keyof T]-?: undefined extends T[P] ? never : P; }[keyof T] +>T : T + + [P in keyof T]-?: undefined extends T[P] ? never : P +>P : P +>T : T +>T : T +>P : P +>P : P + +}[keyof T]; +>T : T + +type OptionalPropNames = { +>OptionalPropNames : { [P in keyof T]-?: undefined extends T[P] ? P : never; }[keyof T] +>T : T + + [P in keyof T]-?: undefined extends T[P] ? P : never +>P : P +>T : T +>T : T +>P : P +>P : P + +}[keyof T]; +>T : T + +type RequiredProps = { [P in RequiredPropNames]: T[P] }; +>RequiredProps : RequiredProps +>T : T +>P : P +>RequiredPropNames : { [P in keyof T]-?: undefined extends T[P] ? never : P; }[keyof T] +>T : T +>T : T +>P : P + +type OptionalProps = { [P in OptionalPropNames]?: T[P] }; +>OptionalProps : OptionalProps +>T : T +>P : P +>OptionalPropNames : { [P in keyof T]-?: undefined extends T[P] ? P : never; }[keyof T] +>T : T +>T : T +>P : P + +type Match = [Exp] extends [Act] +>Match : Match +>Exp : Exp +>Act : Act +>Exp : Exp +>Act : Act + + ? ([Act] extends [Exp] ? "Match" : "Did not match 2") +>Act : Act +>Exp : Exp + + : "Did not match 1"; + +type ExpectType = Match extends "Match" +>ExpectType : ExpectType +>Exp : Exp +>Act : Act +>Match : Match +>Exp : Exp +>Act : Act + + ? ({} extends Exp ? Match, Required> : "Match") +>Exp : Exp +>Match : Match +>Required : Required +>Exp : Exp +>Required : Required +>Act : Act + + : "Did not match"; + +type P3 = { a: string; b: number; c?: boolean }; +>P3 : P3 +>a : string +>b : number +>c : boolean | undefined + +type P2 = { a: string; c?: boolean }; +>P2 : P2 +>a : string +>c : boolean | undefined + +type P1 = { c?: boolean }; +>P1 : P1 +>c : boolean | undefined + +type P0 = {}; +>P0 : P0 + +type P3Names = RequiredPropNames; // expect 'a' | 'b' +>P3Names : "a" | "b" +>RequiredPropNames : { [P in keyof T]-?: undefined extends T[P] ? never : P; }[keyof T] +>P3 : P3 + +type P2Names = RequiredPropNames; // expect 'a' +>P2Names : "a" +>RequiredPropNames : { [P in keyof T]-?: undefined extends T[P] ? never : P; }[keyof T] +>P2 : P2 + +type P1Names = RequiredPropNames; // expect never +>P1Names : never +>RequiredPropNames : { [P in keyof T]-?: undefined extends T[P] ? never : P; }[keyof T] +>P1 : P1 + +type P0Names = RequiredPropNames; // expect never +>P0Names : never +>RequiredPropNames : { [P in keyof T]-?: undefined extends T[P] ? never : P; }[keyof T] +>P0 : P0 + +declare const p3NameTest: ExpectType<"a" | "b", P3Names>; +>p3NameTest : "Match" +>ExpectType : ExpectType +>P3Names : "a" | "b" + +declare const p2NameTest: ExpectType<"a", P2Names>; +>p2NameTest : "Match" +>ExpectType : ExpectType +>P2Names : "a" + +declare const p1NameTest: ExpectType; +>p1NameTest : "Match" +>ExpectType : ExpectType +>P1Names : never + +declare const p0NameTest: ExpectType; +>p0NameTest : "Match" +>ExpectType : ExpectType +>P0Names : never + +type P3Props = RequiredProps; // expect { a: string; b: number } +>P3Props : RequiredProps +>RequiredProps : RequiredProps +>P3 : P3 + +type P2Props = RequiredProps; // expect { a: string; } +>P2Props : RequiredProps +>RequiredProps : RequiredProps +>P2 : P2 + +type P1Props = RequiredProps; // expect {} +>P1Props : RequiredProps +>RequiredProps : RequiredProps +>P1 : P1 + +type P0Props = RequiredProps; // expect {} +>P0Props : RequiredProps +>RequiredProps : RequiredProps +>P0 : P0 + +declare const p3Test: ExpectType<{ a: string; b: number }, P3Props>; +>p3Test : "Match" +>ExpectType : ExpectType +>a : string +>b : number +>P3Props : RequiredProps + +declare const p2Test: ExpectType<{ a: string }, P2Props>; +>p2Test : "Match" +>ExpectType : ExpectType +>a : string +>P2Props : RequiredProps + +declare const p1Test: ExpectType<{}, P1Props>; +>p1Test : "Match" +>ExpectType : ExpectType +>P1Props : RequiredProps + +declare const p0Test: ExpectType<{}, P0Props>; +>p0Test : "Match" +>ExpectType : ExpectType +>P0Props : RequiredProps + +type O3 = { a?: string; b?: number; c: boolean }; +>O3 : O3 +>a : string | undefined +>b : number | undefined +>c : boolean + +type O2 = { a?: string; c: boolean }; +>O2 : O2 +>a : string | undefined +>c : boolean + +type O1 = { c: boolean }; +>O1 : O1 +>c : boolean + +type O0 = {}; +>O0 : O0 + +type O3Names = OptionalPropNames; // expect 'a' | 'b' +>O3Names : "a" | "b" +>OptionalPropNames : { [P in keyof T]-?: undefined extends T[P] ? P : never; }[keyof T] +>O3 : O3 + +type O2Names = OptionalPropNames; // expect 'a' +>O2Names : "a" +>OptionalPropNames : { [P in keyof T]-?: undefined extends T[P] ? P : never; }[keyof T] +>O2 : O2 + +type O1Names = OptionalPropNames; // expect never +>O1Names : never +>OptionalPropNames : { [P in keyof T]-?: undefined extends T[P] ? P : never; }[keyof T] +>O1 : O1 + +type O0Names = OptionalPropNames; // expect never +>O0Names : never +>OptionalPropNames : { [P in keyof T]-?: undefined extends T[P] ? P : never; }[keyof T] +>O0 : O0 + +declare const o3NameTest: ExpectType<"a" | "b", O3Names>; +>o3NameTest : "Match" +>ExpectType : ExpectType +>O3Names : "a" | "b" + +declare const o2NameTest: ExpectType<"a", O2Names>; +>o2NameTest : "Match" +>ExpectType : ExpectType +>O2Names : "a" + +declare const o1NameTest: ExpectType; +>o1NameTest : "Match" +>ExpectType : ExpectType +>O1Names : never + +declare const o0NameTest: ExpectType; +>o0NameTest : "Match" +>ExpectType : ExpectType +>O0Names : never + +type O3Props = OptionalProps; // expect { a?: string | undefined; b?: number | undefined } +>O3Props : OptionalProps +>OptionalProps : OptionalProps +>O3 : O3 + +type O2Props = OptionalProps; // expect { a?: string | undefined; } +>O2Props : OptionalProps +>OptionalProps : OptionalProps +>O2 : O2 + +type O1Props = OptionalProps; // expect {} +>O1Props : OptionalProps +>OptionalProps : OptionalProps +>O1 : O1 + +type O0Props = OptionalProps; // expect {} +>O0Props : OptionalProps +>OptionalProps : OptionalProps +>O0 : O0 + +declare const o3Test: ExpectType<{ a?: string; b?: number }, O3Props>; +>o3Test : "Match" +>ExpectType : ExpectType +>a : string | undefined +>b : number | undefined +>O3Props : OptionalProps + +declare const o2Test: ExpectType<{ a?: string }, O2Props>; +>o2Test : "Match" +>ExpectType : ExpectType +>a : string | undefined +>O2Props : OptionalProps + +declare const o1Test: ExpectType<{}, O1Props>; +>o1Test : "Match" +>ExpectType : ExpectType +>O1Props : OptionalProps + +declare const o0Test: ExpectType<{}, O0Props>; +>o0Test : "Match" +>ExpectType : ExpectType +>O0Props : OptionalProps + diff --git a/tests/cases/compiler/indexingTypesWithNever.ts b/tests/cases/compiler/indexingTypesWithNever.ts new file mode 100644 index 0000000000000..9044ded2ff4c3 --- /dev/null +++ b/tests/cases/compiler/indexingTypesWithNever.ts @@ -0,0 +1,111 @@ +// @strict: true + +type TestObj = { + a: string; + b: number; +}; + +// Should be never but without an error +type Result1 = TestObj[never]; + +type EmptyObj = {}; + +// Should be never but without an error +type Result2 = EmptyObj[keyof EmptyObj]; + +declare function genericFn1(obj: T): T[never]; + +// Should be never +const result3 = genericFn1({ c: "ctest", d: "dtest" }); + +declare function genericFn2( + obj: T +): T[never]; + +// Should be never +const result4 = genericFn2({ e: "etest", f: "ftest" }); + +declare function genericFn3< + T extends { [K in keyof T]: T[K] }, + U extends keyof T, + V extends keyof T +>(obj: T, u: U, v: V): T[U & V]; + +// Should be never +const result5 = genericFn3({ g: "gtest", h: "htest" }, "g", "h"); // 'g' & 'h' will reduce to never + + +declare const obj: {a: string, b: number} +declare const key: never + +const result6 = obj[key] + +// Expanded examples from https://github.com/Microsoft/TypeScript/issues/21988 +type RequiredPropNames = { + [P in keyof T]-?: undefined extends T[P] ? never : P +}[keyof T]; + +type OptionalPropNames = { + [P in keyof T]-?: undefined extends T[P] ? P : never +}[keyof T]; + +type RequiredProps = { [P in RequiredPropNames]: T[P] }; +type OptionalProps = { [P in OptionalPropNames]?: T[P] }; + +type Match = [Exp] extends [Act] + ? ([Act] extends [Exp] ? "Match" : "Did not match 2") + : "Did not match 1"; + +type ExpectType = Match extends "Match" + ? ({} extends Exp ? Match, Required> : "Match") + : "Did not match"; + +type P3 = { a: string; b: number; c?: boolean }; +type P2 = { a: string; c?: boolean }; +type P1 = { c?: boolean }; +type P0 = {}; + +type P3Names = RequiredPropNames; // expect 'a' | 'b' +type P2Names = RequiredPropNames; // expect 'a' +type P1Names = RequiredPropNames; // expect never +type P0Names = RequiredPropNames; // expect never + +declare const p3NameTest: ExpectType<"a" | "b", P3Names>; +declare const p2NameTest: ExpectType<"a", P2Names>; +declare const p1NameTest: ExpectType; +declare const p0NameTest: ExpectType; + +type P3Props = RequiredProps; // expect { a: string; b: number } +type P2Props = RequiredProps; // expect { a: string; } +type P1Props = RequiredProps; // expect {} +type P0Props = RequiredProps; // expect {} + +declare const p3Test: ExpectType<{ a: string; b: number }, P3Props>; +declare const p2Test: ExpectType<{ a: string }, P2Props>; +declare const p1Test: ExpectType<{}, P1Props>; +declare const p0Test: ExpectType<{}, P0Props>; + +type O3 = { a?: string; b?: number; c: boolean }; +type O2 = { a?: string; c: boolean }; +type O1 = { c: boolean }; +type O0 = {}; + +type O3Names = OptionalPropNames; // expect 'a' | 'b' +type O2Names = OptionalPropNames; // expect 'a' +type O1Names = OptionalPropNames; // expect never +type O0Names = OptionalPropNames; // expect never + +declare const o3NameTest: ExpectType<"a" | "b", O3Names>; +declare const o2NameTest: ExpectType<"a", O2Names>; +declare const o1NameTest: ExpectType; +declare const o0NameTest: ExpectType; + +type O3Props = OptionalProps; // expect { a?: string | undefined; b?: number | undefined } +type O2Props = OptionalProps; // expect { a?: string | undefined; } +type O1Props = OptionalProps; // expect {} +type O0Props = OptionalProps; // expect {} + +declare const o3Test: ExpectType<{ a?: string; b?: number }, O3Props>; +declare const o2Test: ExpectType<{ a?: string }, O2Props>; +declare const o1Test: ExpectType<{}, O1Props>; +declare const o0Test: ExpectType<{}, O0Props>;