Skip to content

Commit

Permalink
Fixed accidental undefined omissions in union props sourced from in…
Browse files Browse the repository at this point in the history
…dex type under `noUncheckedIndexedAccess`
  • Loading branch information
Andarist committed Feb 20, 2025
1 parent 3f416e0 commit 464abcf
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 2 deletions.
9 changes: 7 additions & 2 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15034,6 +15034,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
propTypes.push(type);
}
addRange(propTypes, indexTypes);
if (indexTypes && compilerOptions.noUncheckedIndexedAccess) {
append(propTypes, missingType);
}
const result = createSymbol(SymbolFlags.Property | (optionalFlag ?? 0), name, syntheticFlag | checkFlags);
result.links.containingType = containingType;
if (!hasNonUniformValueDeclaration && firstValueDeclaration) {
Expand Down Expand Up @@ -27576,6 +27579,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 ?
Expand Down Expand Up @@ -34193,8 +34198,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));
Expand Down
40 changes: 40 additions & 0 deletions tests/baselines/reference/noUncheckedIndexAccessUnionProp1.symbols
Original file line number Diff line number Diff line change
@@ -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, --, --))
>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, --, --))
>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, --, --))
>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))

105 changes: 105 additions & 0 deletions tests/baselines/reference/noUncheckedIndexAccessUnionProp1.types
Original file line number Diff line number Diff line change
@@ -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
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^

11 changes: 11 additions & 0 deletions tests/cases/compiler/noUncheckedIndexAccessUnionProp1.ts
Original file line number Diff line number Diff line change
@@ -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;

0 comments on commit 464abcf

Please sign in to comment.