diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 74663e107cc66..44e5605b23260 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -15645,6 +15645,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { propTypes.push(type); } addRange(propTypes, indexTypes); + if (indexTypes && compilerOptions.noUncheckedIndexedAccess) { + append(propTypes, missingType); + } const result = createSymbol(propFlags | (optionalFlag ?? 0), name, syntheticFlag | checkFlags); result.links.containingType = containingType; if (!hasNonUniformValueDeclaration && firstValueDeclaration) { @@ -28223,6 +28226,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { errorType; } + function includeUndefinedInIndexSignature(type: Type): Type; + function includeUndefinedInIndexSignature(type: Type | undefined): Type | undefined; function includeUndefinedInIndexSignature(type: Type | undefined): Type | undefined { if (!type) return type; return compilerOptions.noUncheckedIndexedAccess ? @@ -34873,8 +34878,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } propType = indexInfo.type; - if (compilerOptions.noUncheckedIndexedAccess && getAssignmentTargetKind(node) !== AssignmentKind.Definite) { - propType = getUnionType([propType, missingType]); + if (getAssignmentTargetKind(node) !== AssignmentKind.Definite) { + propType = includeUndefinedInIndexSignature(propType); } if (compilerOptions.noPropertyAccessFromIndexSignature && isPropertyAccessExpression(node)) { error(right, Diagnostics.Property_0_comes_from_an_index_signature_so_it_must_be_accessed_with_0, unescapeLeadingUnderscores(right.escapedText)); diff --git a/tests/baselines/reference/noUncheckedIndexAccessUnionProp1.symbols b/tests/baselines/reference/noUncheckedIndexAccessUnionProp1.symbols new file mode 100644 index 0000000000000..69006bd969405 --- /dev/null +++ b/tests/baselines/reference/noUncheckedIndexAccessUnionProp1.symbols @@ -0,0 +1,40 @@ +//// [tests/cases/compiler/noUncheckedIndexAccessUnionProp1.ts] //// + +=== noUncheckedIndexAccessUnionProp1.ts === +// https://github.com/microsoft/TypeScript/issues/61225 + +const nums: { [k: string]: number } = Math.random() < 0.5 ? { a: 1 } : { b: 2 }; +>nums : Symbol(nums, Decl(noUncheckedIndexAccessUnionProp1.ts, 2, 5)) +>k : Symbol(k, Decl(noUncheckedIndexAccessUnionProp1.ts, 2, 15)) +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>a : Symbol(a, Decl(noUncheckedIndexAccessUnionProp1.ts, 2, 61)) +>b : Symbol(b, Decl(noUncheckedIndexAccessUnionProp1.ts, 2, 72)) + +const str = { a: "hello" }; +>str : Symbol(str, Decl(noUncheckedIndexAccessUnionProp1.ts, 3, 5)) +>a : Symbol(a, Decl(noUncheckedIndexAccessUnionProp1.ts, 3, 13)) + +const hmm = Math.random() < 0.5 ? nums.a : str.a; +>hmm : Symbol(hmm, Decl(noUncheckedIndexAccessUnionProp1.ts, 5, 5)) +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>nums.a : Symbol(__index, Decl(noUncheckedIndexAccessUnionProp1.ts, 2, 13)) +>nums : Symbol(nums, Decl(noUncheckedIndexAccessUnionProp1.ts, 2, 5)) +>a : Symbol(__index, Decl(noUncheckedIndexAccessUnionProp1.ts, 2, 13)) +>str.a : Symbol(a, Decl(noUncheckedIndexAccessUnionProp1.ts, 3, 13)) +>str : Symbol(str, Decl(noUncheckedIndexAccessUnionProp1.ts, 3, 5)) +>a : Symbol(a, Decl(noUncheckedIndexAccessUnionProp1.ts, 3, 13)) + +const wha = (Math.random() < 0.5 ? nums : str).a; +>wha : Symbol(wha, Decl(noUncheckedIndexAccessUnionProp1.ts, 6, 5)) +>(Math.random() < 0.5 ? nums : str).a : Symbol(a, Decl(noUncheckedIndexAccessUnionProp1.ts, 3, 13)) +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>nums : Symbol(nums, Decl(noUncheckedIndexAccessUnionProp1.ts, 2, 5)) +>str : Symbol(str, Decl(noUncheckedIndexAccessUnionProp1.ts, 3, 5)) +>a : Symbol(a, Decl(noUncheckedIndexAccessUnionProp1.ts, 3, 13)) + diff --git a/tests/baselines/reference/noUncheckedIndexAccessUnionProp1.types b/tests/baselines/reference/noUncheckedIndexAccessUnionProp1.types new file mode 100644 index 0000000000000..bb916b32e3c87 --- /dev/null +++ b/tests/baselines/reference/noUncheckedIndexAccessUnionProp1.types @@ -0,0 +1,105 @@ +//// [tests/cases/compiler/noUncheckedIndexAccessUnionProp1.ts] //// + +=== noUncheckedIndexAccessUnionProp1.ts === +// https://github.com/microsoft/TypeScript/issues/61225 + +const nums: { [k: string]: number } = Math.random() < 0.5 ? { a: 1 } : { b: 2 }; +>nums : { [k: string]: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^ +>k : string +> : ^^^^^^ +>Math.random() < 0.5 ? { a: 1 } : { b: 2 } : { a: number; } | { b: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>Math.random() < 0.5 : boolean +> : ^^^^^^^ +>Math.random() : number +> : ^^^^^^ +>Math.random : () => number +> : ^^^^^^ +>Math : Math +> : ^^^^ +>random : () => number +> : ^^^^^^ +>0.5 : 0.5 +> : ^^^ +>{ a: 1 } : { a: number; } +> : ^^^^^^^^^^^^^^ +>a : number +> : ^^^^^^ +>1 : 1 +> : ^ +>{ b: 2 } : { b: number; } +> : ^^^^^^^^^^^^^^ +>b : number +> : ^^^^^^ +>2 : 2 +> : ^ + +const str = { a: "hello" }; +>str : { a: string; } +> : ^^^^^^^^^^^^^^ +>{ a: "hello" } : { a: string; } +> : ^^^^^^^^^^^^^^ +>a : string +> : ^^^^^^ +>"hello" : "hello" +> : ^^^^^^^ + +const hmm = Math.random() < 0.5 ? nums.a : str.a; +>hmm : string | number | undefined +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>Math.random() < 0.5 ? nums.a : str.a : string | number | undefined +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>Math.random() < 0.5 : boolean +> : ^^^^^^^ +>Math.random() : number +> : ^^^^^^ +>Math.random : () => number +> : ^^^^^^ +>Math : Math +> : ^^^^ +>random : () => number +> : ^^^^^^ +>0.5 : 0.5 +> : ^^^ +>nums.a : number | undefined +> : ^^^^^^^^^^^^^^^^^^ +>nums : { [k: string]: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^ +>a : number | undefined +> : ^^^^^^^^^^^^^^^^^^ +>str.a : string +> : ^^^^^^ +>str : { a: string; } +> : ^^^^^^^^^^^^^^ +>a : string +> : ^^^^^^ + +const wha = (Math.random() < 0.5 ? nums : str).a; +>wha : string | number | undefined +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>(Math.random() < 0.5 ? nums : str).a : string | number | undefined +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>(Math.random() < 0.5 ? nums : str) : { [k: string]: number; } | { a: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>Math.random() < 0.5 ? nums : str : { [k: string]: number; } | { a: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>Math.random() < 0.5 : boolean +> : ^^^^^^^ +>Math.random() : number +> : ^^^^^^ +>Math.random : () => number +> : ^^^^^^ +>Math : Math +> : ^^^^ +>random : () => number +> : ^^^^^^ +>0.5 : 0.5 +> : ^^^ +>nums : { [k: string]: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^ +>str : { a: string; } +> : ^^^^^^^^^^^^^^ +>a : string | number | undefined +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + diff --git a/tests/cases/compiler/noUncheckedIndexAccessUnionProp1.ts b/tests/cases/compiler/noUncheckedIndexAccessUnionProp1.ts new file mode 100644 index 0000000000000..922a7f2bf33b5 --- /dev/null +++ b/tests/cases/compiler/noUncheckedIndexAccessUnionProp1.ts @@ -0,0 +1,11 @@ +// @strict: true +// @noUncheckedIndexedAccess: true +// @noEmit: true + +// https://github.com/microsoft/TypeScript/issues/61225 + +const nums: { [k: string]: number } = Math.random() < 0.5 ? { a: 1 } : { b: 2 }; +const str = { a: "hello" }; + +const hmm = Math.random() < 0.5 ? nums.a : str.a; +const wha = (Math.random() < 0.5 ? nums : str).a;