diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0d12a7f74fdb4..fa400705e606b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -18196,6 +18196,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (objectType.flags & (TypeFlags.Any | TypeFlags.Never)) { return objectType; } + if (indexType === neverType || indexType === implicitNeverType) { + return neverType; + } // If no index signature is applicable, we default to the string index signature. In effect, this means the string // index signature applies even when accessing with a symbol-like type. const indexInfo = getApplicableIndexInfo(objectType, indexType) || getIndexInfoOfType(objectType, stringType); @@ -18232,9 +18235,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } return indexInfo.type; } - if (indexType.flags & TypeFlags.Never) { - return neverType; - } if (isJSLiteralType(objectType)) { return anyType; } @@ -18537,7 +18537,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { objectType = getReducedType(objectType); // If the object type has a string index signature and no other members we know that the result will // always be the type of that index signature and we can simplify accordingly. - if (isStringIndexSignatureOnlyType(objectType) && !(indexType.flags & TypeFlags.Nullable) && isTypeAssignableToKind(indexType, TypeFlags.String | TypeFlags.Number)) { + if (isStringIndexSignatureOnlyType(objectType) && !(indexType.flags & TypeFlags.Nullable) && !(indexType.flags & TypeFlags.Never) && isTypeAssignableToKind(indexType, TypeFlags.String | TypeFlags.Number)) { indexType = stringType; } // In noUncheckedIndexedAccess mode, indexed access operations that occur in an expression in a read position and resolve to diff --git a/tests/baselines/reference/indexIntoArrayTupleObjectWithNever.js b/tests/baselines/reference/indexIntoArrayTupleObjectWithNever.js new file mode 100644 index 0000000000000..b0fea1a60cf82 --- /dev/null +++ b/tests/baselines/reference/indexIntoArrayTupleObjectWithNever.js @@ -0,0 +1,78 @@ +//// [tests/cases/compiler/indexIntoArrayTupleObjectWithNever.ts] //// + +//// [indexIntoArrayTupleObjectWithNever.ts] +type A = { a: 42 }[never] + +type B = [42][never] + +type C = Array<42>[never] + +type D = 42[never] + +type E = any[never] // any + + +// ----------------------------------------------------------------- + +type Indexes = keyof T & `${number}` + +declare namespace IfNoDuplicateElements { + export type Duplicate = "Duplicate" + export type NotDuplicate = "NotDuplicate" + export type HasDuplicates = { + [K in Indexes]: T[K] extends T[Exclude, K>] ? Duplicate : NotDuplicate + }[Indexes] +} + +export type IfNoDuplicateElements< + T extends readonly any[], + True = T, + False = never +> = IfNoDuplicateElements.Duplicate extends IfNoDuplicateElements.HasDuplicates ? False : True + +type T0 = IfNoDuplicateElements<[1], true, false> +type T1 = IfNoDuplicateElements<[1,1], true, false> + +// ----------------------------------------------------------------- + +type T2 = Record[never] +type T3 = Record<`--${string}`, boolean>[never] + + +const obj = { + arr: [], +}; + +const objWithIndex: Record = {}; + +const el = obj.arr[0]; +const result = objWithIndex[el]; + +function testUnreachableNeverType(smt: number | string) { + if(typeof smt === "number") {} + else if(typeof smt === "string") {} + else { + const result = objWithIndex[smt] + } +} + +// ----------------------------------------------------------------- + + +//// [indexIntoArrayTupleObjectWithNever.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var obj = { + arr: [], +}; +var objWithIndex = {}; +var el = obj.arr[0]; +var result = objWithIndex[el]; +function testUnreachableNeverType(smt) { + if (typeof smt === "number") { } + else if (typeof smt === "string") { } + else { + var result_1 = objWithIndex[smt]; + } +} +// ----------------------------------------------------------------- diff --git a/tests/baselines/reference/indexIntoArrayTupleObjectWithNever.symbols b/tests/baselines/reference/indexIntoArrayTupleObjectWithNever.symbols new file mode 100644 index 0000000000000..c34dff5ac9f8f --- /dev/null +++ b/tests/baselines/reference/indexIntoArrayTupleObjectWithNever.symbols @@ -0,0 +1,144 @@ +//// [tests/cases/compiler/indexIntoArrayTupleObjectWithNever.ts] //// + +=== indexIntoArrayTupleObjectWithNever.ts === +type A = { a: 42 }[never] +>A : Symbol(A, Decl(indexIntoArrayTupleObjectWithNever.ts, 0, 0)) +>a : Symbol(a, Decl(indexIntoArrayTupleObjectWithNever.ts, 0, 10)) + +type B = [42][never] +>B : Symbol(B, Decl(indexIntoArrayTupleObjectWithNever.ts, 0, 25)) + +type C = Array<42>[never] +>C : Symbol(C, Decl(indexIntoArrayTupleObjectWithNever.ts, 2, 20)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + +type D = 42[never] +>D : Symbol(D, Decl(indexIntoArrayTupleObjectWithNever.ts, 4, 25)) + +type E = any[never] // any +>E : Symbol(E, Decl(indexIntoArrayTupleObjectWithNever.ts, 6, 18)) + + +// ----------------------------------------------------------------- + +type Indexes = keyof T & `${number}` +>Indexes : Symbol(Indexes, Decl(indexIntoArrayTupleObjectWithNever.ts, 8, 19)) +>T : Symbol(T, Decl(indexIntoArrayTupleObjectWithNever.ts, 13, 13)) +>T : Symbol(T, Decl(indexIntoArrayTupleObjectWithNever.ts, 13, 13)) + +declare namespace IfNoDuplicateElements { +>IfNoDuplicateElements : Symbol(IfNoDuplicateElements, Decl(indexIntoArrayTupleObjectWithNever.ts, 13, 62), Decl(indexIntoArrayTupleObjectWithNever.ts, 21, 1)) + + export type Duplicate = "Duplicate" +>Duplicate : Symbol(Duplicate, Decl(indexIntoArrayTupleObjectWithNever.ts, 15, 41)) + + export type NotDuplicate = "NotDuplicate" +>NotDuplicate : Symbol(NotDuplicate, Decl(indexIntoArrayTupleObjectWithNever.ts, 16, 39)) + + export type HasDuplicates = { +>HasDuplicates : Symbol(HasDuplicates, Decl(indexIntoArrayTupleObjectWithNever.ts, 17, 45)) +>T : Symbol(T, Decl(indexIntoArrayTupleObjectWithNever.ts, 18, 30)) + + [K in Indexes]: T[K] extends T[Exclude, K>] ? Duplicate : NotDuplicate +>K : Symbol(K, Decl(indexIntoArrayTupleObjectWithNever.ts, 19, 10)) +>Indexes : Symbol(Indexes, Decl(indexIntoArrayTupleObjectWithNever.ts, 8, 19)) +>T : Symbol(T, Decl(indexIntoArrayTupleObjectWithNever.ts, 18, 30)) +>T : Symbol(T, Decl(indexIntoArrayTupleObjectWithNever.ts, 18, 30)) +>K : Symbol(K, Decl(indexIntoArrayTupleObjectWithNever.ts, 19, 10)) +>T : Symbol(T, Decl(indexIntoArrayTupleObjectWithNever.ts, 18, 30)) +>Exclude : Symbol(Exclude, Decl(lib.es5.d.ts, --, --)) +>Indexes : Symbol(Indexes, Decl(indexIntoArrayTupleObjectWithNever.ts, 8, 19)) +>T : Symbol(T, Decl(indexIntoArrayTupleObjectWithNever.ts, 18, 30)) +>K : Symbol(K, Decl(indexIntoArrayTupleObjectWithNever.ts, 19, 10)) +>Duplicate : Symbol(Duplicate, Decl(indexIntoArrayTupleObjectWithNever.ts, 15, 41)) +>NotDuplicate : Symbol(NotDuplicate, Decl(indexIntoArrayTupleObjectWithNever.ts, 16, 39)) + + }[Indexes] +>Indexes : Symbol(Indexes, Decl(indexIntoArrayTupleObjectWithNever.ts, 8, 19)) +>T : Symbol(T, Decl(indexIntoArrayTupleObjectWithNever.ts, 18, 30)) +} + +export type IfNoDuplicateElements< +>IfNoDuplicateElements : Symbol(IfNoDuplicateElements, Decl(indexIntoArrayTupleObjectWithNever.ts, 21, 1)) + + T extends readonly any[], +>T : Symbol(T, Decl(indexIntoArrayTupleObjectWithNever.ts, 23, 34)) + + True = T, +>True : Symbol(True, Decl(indexIntoArrayTupleObjectWithNever.ts, 24, 29)) +>T : Symbol(T, Decl(indexIntoArrayTupleObjectWithNever.ts, 23, 34)) + + False = never +>False : Symbol(False, Decl(indexIntoArrayTupleObjectWithNever.ts, 25, 13)) + +> = IfNoDuplicateElements.Duplicate extends IfNoDuplicateElements.HasDuplicates ? False : True +>IfNoDuplicateElements : Symbol(IfNoDuplicateElements, Decl(indexIntoArrayTupleObjectWithNever.ts, 13, 62), Decl(indexIntoArrayTupleObjectWithNever.ts, 21, 1)) +>Duplicate : Symbol(IfNoDuplicateElements.Duplicate, Decl(indexIntoArrayTupleObjectWithNever.ts, 15, 41)) +>IfNoDuplicateElements : Symbol(IfNoDuplicateElements, Decl(indexIntoArrayTupleObjectWithNever.ts, 13, 62), Decl(indexIntoArrayTupleObjectWithNever.ts, 21, 1)) +>HasDuplicates : Symbol(IfNoDuplicateElements.HasDuplicates, Decl(indexIntoArrayTupleObjectWithNever.ts, 17, 45)) +>T : Symbol(T, Decl(indexIntoArrayTupleObjectWithNever.ts, 23, 34)) +>False : Symbol(False, Decl(indexIntoArrayTupleObjectWithNever.ts, 25, 13)) +>True : Symbol(True, Decl(indexIntoArrayTupleObjectWithNever.ts, 24, 29)) + +type T0 = IfNoDuplicateElements<[1], true, false> +>T0 : Symbol(T0, Decl(indexIntoArrayTupleObjectWithNever.ts, 27, 97)) +>IfNoDuplicateElements : Symbol(IfNoDuplicateElements, Decl(indexIntoArrayTupleObjectWithNever.ts, 21, 1)) + +type T1 = IfNoDuplicateElements<[1,1], true, false> +>T1 : Symbol(T1, Decl(indexIntoArrayTupleObjectWithNever.ts, 29, 49)) +>IfNoDuplicateElements : Symbol(IfNoDuplicateElements, Decl(indexIntoArrayTupleObjectWithNever.ts, 21, 1)) + +// ----------------------------------------------------------------- + +type T2 = Record[never] +>T2 : Symbol(T2, Decl(indexIntoArrayTupleObjectWithNever.ts, 30, 51)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) + +type T3 = Record<`--${string}`, boolean>[never] +>T3 : Symbol(T3, Decl(indexIntoArrayTupleObjectWithNever.ts, 34, 40)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) + + +const obj = { +>obj : Symbol(obj, Decl(indexIntoArrayTupleObjectWithNever.ts, 38, 5)) + + arr: [], +>arr : Symbol(arr, Decl(indexIntoArrayTupleObjectWithNever.ts, 38, 13)) + +}; + +const objWithIndex: Record = {}; +>objWithIndex : Symbol(objWithIndex, Decl(indexIntoArrayTupleObjectWithNever.ts, 42, 5)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) + +const el = obj.arr[0]; +>el : Symbol(el, Decl(indexIntoArrayTupleObjectWithNever.ts, 44, 5)) +>obj.arr : Symbol(arr, Decl(indexIntoArrayTupleObjectWithNever.ts, 38, 13)) +>obj : Symbol(obj, Decl(indexIntoArrayTupleObjectWithNever.ts, 38, 5)) +>arr : Symbol(arr, Decl(indexIntoArrayTupleObjectWithNever.ts, 38, 13)) + +const result = objWithIndex[el]; +>result : Symbol(result, Decl(indexIntoArrayTupleObjectWithNever.ts, 45, 5)) +>objWithIndex : Symbol(objWithIndex, Decl(indexIntoArrayTupleObjectWithNever.ts, 42, 5)) +>el : Symbol(el, Decl(indexIntoArrayTupleObjectWithNever.ts, 44, 5)) + +function testUnreachableNeverType(smt: number | string) { +>testUnreachableNeverType : Symbol(testUnreachableNeverType, Decl(indexIntoArrayTupleObjectWithNever.ts, 45, 32)) +>smt : Symbol(smt, Decl(indexIntoArrayTupleObjectWithNever.ts, 47, 34)) + + if(typeof smt === "number") {} +>smt : Symbol(smt, Decl(indexIntoArrayTupleObjectWithNever.ts, 47, 34)) + + else if(typeof smt === "string") {} +>smt : Symbol(smt, Decl(indexIntoArrayTupleObjectWithNever.ts, 47, 34)) + + else { + const result = objWithIndex[smt] +>result : Symbol(result, Decl(indexIntoArrayTupleObjectWithNever.ts, 51, 9)) +>objWithIndex : Symbol(objWithIndex, Decl(indexIntoArrayTupleObjectWithNever.ts, 42, 5)) +>smt : Symbol(smt, Decl(indexIntoArrayTupleObjectWithNever.ts, 47, 34)) + } +} + +// ----------------------------------------------------------------- + diff --git a/tests/baselines/reference/indexIntoArrayTupleObjectWithNever.types b/tests/baselines/reference/indexIntoArrayTupleObjectWithNever.types new file mode 100644 index 0000000000000..1f9118ddd2403 --- /dev/null +++ b/tests/baselines/reference/indexIntoArrayTupleObjectWithNever.types @@ -0,0 +1,123 @@ +//// [tests/cases/compiler/indexIntoArrayTupleObjectWithNever.ts] //// + +=== indexIntoArrayTupleObjectWithNever.ts === +type A = { a: 42 }[never] +>A : never +>a : 42 + +type B = [42][never] +>B : never + +type C = Array<42>[never] +>C : never + +type D = 42[never] +>D : never + +type E = any[never] // any +>E : any + + +// ----------------------------------------------------------------- + +type Indexes = keyof T & `${number}` +>Indexes : Indexes + +declare namespace IfNoDuplicateElements { + export type Duplicate = "Duplicate" +>Duplicate : "Duplicate" + + export type NotDuplicate = "NotDuplicate" +>NotDuplicate : "NotDuplicate" + + export type HasDuplicates = { +>HasDuplicates : HasDuplicates + + [K in Indexes]: T[K] extends T[Exclude, K>] ? Duplicate : NotDuplicate + }[Indexes] +} + +export type IfNoDuplicateElements< +>IfNoDuplicateElements : IfNoDuplicateElements + + T extends readonly any[], + True = T, + False = never +> = IfNoDuplicateElements.Duplicate extends IfNoDuplicateElements.HasDuplicates ? False : True +>IfNoDuplicateElements : any +>IfNoDuplicateElements : any + +type T0 = IfNoDuplicateElements<[1], true, false> +>T0 : true +>true : true +>false : false + +type T1 = IfNoDuplicateElements<[1,1], true, false> +>T1 : false +>true : true +>false : false + +// ----------------------------------------------------------------- + +type T2 = Record[never] +>T2 : never + +type T3 = Record<`--${string}`, boolean>[never] +>T3 : never + + +const obj = { +>obj : { arr: never[]; } +>{ arr: [],} : { arr: never[]; } + + arr: [], +>arr : never[] +>[] : never[] + +}; + +const objWithIndex: Record = {}; +>objWithIndex : Record +>{} : {} + +const el = obj.arr[0]; +>el : never +>obj.arr[0] : never +>obj.arr : never[] +>obj : { arr: never[]; } +>arr : never[] +>0 : 0 + +const result = objWithIndex[el]; +>result : never +>objWithIndex[el] : never +>objWithIndex : Record +>el : never + +function testUnreachableNeverType(smt: number | string) { +>testUnreachableNeverType : (smt: number | string) => void +>smt : string | number + + if(typeof smt === "number") {} +>typeof smt === "number" : boolean +>typeof smt : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>smt : string | number +>"number" : "number" + + else if(typeof smt === "string") {} +>typeof smt === "string" : boolean +>typeof smt : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>smt : string +>"string" : "string" + + else { + const result = objWithIndex[smt] +>result : never +>objWithIndex[smt] : never +>objWithIndex : Record +>smt : never + } +} + +// ----------------------------------------------------------------- + diff --git a/tests/cases/compiler/indexIntoArrayTupleObjectWithNever.ts b/tests/cases/compiler/indexIntoArrayTupleObjectWithNever.ts new file mode 100644 index 0000000000000..86c8dbc84cb89 --- /dev/null +++ b/tests/cases/compiler/indexIntoArrayTupleObjectWithNever.ts @@ -0,0 +1,58 @@ +// @strict: true + +type A = { a: 42 }[never] + +type B = [42][never] + +type C = Array<42>[never] + +type D = 42[never] + +type E = any[never] // any + + +// ----------------------------------------------------------------- + +type Indexes = keyof T & `${number}` + +declare namespace IfNoDuplicateElements { + export type Duplicate = "Duplicate" + export type NotDuplicate = "NotDuplicate" + export type HasDuplicates = { + [K in Indexes]: T[K] extends T[Exclude, K>] ? Duplicate : NotDuplicate + }[Indexes] +} + +export type IfNoDuplicateElements< + T extends readonly any[], + True = T, + False = never +> = IfNoDuplicateElements.Duplicate extends IfNoDuplicateElements.HasDuplicates ? False : True + +type T0 = IfNoDuplicateElements<[1], true, false> +type T1 = IfNoDuplicateElements<[1,1], true, false> + +// ----------------------------------------------------------------- + +type T2 = Record[never] +type T3 = Record<`--${string}`, boolean>[never] + + +const obj = { + arr: [], +}; + +const objWithIndex: Record = {}; + +const el = obj.arr[0]; +const result = objWithIndex[el]; + +function testUnreachableNeverType(smt: number | string) { + if(typeof smt === "number") {} + else if(typeof smt === "string") {} + else { + const result = objWithIndex[smt] + } +} + +// -----------------------------------------------------------------