diff --git a/tsc/internal/checker/flow.go b/tsc/internal/checker/flow.go index d08254cc8ef88..342fac860f594 100644 --- a/tsc/internal/checker/flow.go +++ b/tsc/internal/checker/flow.go @@ -707,7 +707,8 @@ func (c *Checker) narrowTypeByDiscriminantProperty(t *Type, access *ast.Node, op candidate := c.getConstituentTypeForKeyType(t, c.getTypeOfExpression(value)) if candidate != nil { if assumeTrue && operator == ast.KindEqualsEqualsEqualsToken || !assumeTrue && operator == ast.KindExclamationEqualsEqualsToken { - return candidate + // Nullable constituents have no key property; preserve them as the general path does. + return c.getUnionType([]*Type{candidate, c.filterType(t, func(t *Type) bool { return t.flags&TypeFlagsNullable != 0 })}) } if propType := c.getTypeOfPropertyOfType(candidate, keyPropertyName); propType != nil && isUnitType(propType) { return c.removeType(t, candidate) diff --git a/tsc/testdata/baselines/reference/compiler/narrowingUnionWithBang.types b/tsc/testdata/baselines/reference/compiler/narrowingUnionWithBang.types index 0e44a18ee2844..3470ec943bbb3 100644 --- a/tsc/testdata/baselines/reference/compiler/narrowingUnionWithBang.types +++ b/tsc/testdata/baselines/reference/compiler/narrowingUnionWithBang.types @@ -83,9 +83,9 @@ if (working.thing!.name !== "Correct") { >log : (...data: any[]) => void >working.thing!.id : string >working.thing! : { name: 'Correct'; id: string; } ->working.thing : { name: 'Correct'; id: string; } +>working.thing : { name: 'Correct'; id: string; } | undefined >working : WorkingType ->thing : { name: 'Correct'; id: string; } +>thing : { name: 'Correct'; id: string; } | undefined >id : string } diff --git a/tsc/testdata/tests/cases/compiler/discriminatedUnionNonNullAccessNarrowing.ts b/tsc/testdata/tests/cases/compiler/discriminatedUnionNonNullAccessNarrowing.ts new file mode 100644 index 0000000000000..578401a41174a --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/discriminatedUnionNonNullAccessNarrowing.ts @@ -0,0 +1,35 @@ +// @strict: true +// @noEmit: true +// @noTypesAndSymbols: true + +// Discriminant narrowing should be consistent regardless of union size (#62511). + +type Small = + | { type: "1" } + | { type: "2" } + | undefined; + +type Large = + | { type: "1" } + | { type: "2" } + | { type: "3" } + | { type: "4" } + | { type: "5" } + | { type: "6" } + | { type: "7" } + | { type: "8" } + | { type: "9" } + | { type: "10" } + | undefined; + +declare let small: Small; +if (small!.type === "1") { + // @ts-expect-error + small.type; +} + +declare let large: Large; +if (large!.type === "1") { + // @ts-expect-error + large.type; +}