diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index b03cebf741b22..8126cac308459 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -31281,7 +31281,7 @@ func (d *ObjectLiteralDiscriminator) name(index int) string { return d.members[index-len(d.props)].Name() } -func (d *ObjectLiteralDiscriminator) matches(index int, t *Type) bool { +func (d *ObjectLiteralDiscriminator) matches(index int, t *Type, _ bool) bool { var propType *Type if index < len(d.props) { prop := d.props[index] diff --git a/tsc/internal/checker/inference.go b/tsc/internal/checker/inference.go index c9d7351f896df..15b6f7f05b5c8 100644 --- a/tsc/internal/checker/inference.go +++ b/tsc/internal/checker/inference.go @@ -250,7 +250,7 @@ func (c *Checker) inferFromTypes(n *InferenceState, source *Type, target *Type) c.inferToUnionOrIntersectionType(n, source, target) case source.flags&TypeFlagsUnion != 0: if discriminants := c.getInferenceDiscriminants(source, target); len(discriminants) != 0 { - discriminator := &TypeDiscriminator{c: c, props: discriminants, reverse: true, isRelatedTo: c.compareTypesAssignableSimple} + discriminator := &TypeDiscriminator{c: c, props: discriminants, reverse: true, isRelatedTo: c.compareTypesComparableSimple} source = c.discriminateTypeByDiscriminableItems(source, discriminator) } for _, sourceType := range source.Distributed() { @@ -468,7 +468,7 @@ func (c *Checker) inferToUnionOrIntersectionType(n *InferenceState, source *Type filteredTargetsBySource = make([][]*Type, len(sources)) for i, s := range sources { if discriminants := c.getInferenceDiscriminants(target, s); len(discriminants) != 0 { - discriminator := &TypeDiscriminator{c: c, props: discriminants, isRelatedTo: c.compareTypesAssignableSimple} + discriminator := &TypeDiscriminator{c: c, props: discriminants, isRelatedTo: c.compareTypesComparableSimple} if filtered := c.discriminateTypeByDiscriminableItems(target, discriminator); filtered != target { filteredTargetsBySource[i] = filtered.Distributed() } diff --git a/tsc/internal/checker/relater.go b/tsc/internal/checker/relater.go index 3e9c98d9ea95a..9ee7db5c30b9a 100644 --- a/tsc/internal/checker/relater.go +++ b/tsc/internal/checker/relater.go @@ -132,6 +132,13 @@ func (c *Checker) compareTypesAssignableSimple(source *Type, target *Type) Terna return TernaryFalse } +func (c *Checker) compareTypesComparableSimple(source *Type, target *Type) Ternary { + if c.isTypeRelatedTo(source, target, c.comparableRelation) { + return TernaryTrue + } + return TernaryFalse +} + func (c *Checker) compareTypesAssignableWorker(source *Type, target *Type, reportErrors bool) Ternary { if c.isTypeRelatedTo(source, target, c.assignableRelation) { return TernaryTrue @@ -1042,7 +1049,12 @@ func (d *TypeDiscriminator) name(index int) string { return d.props[index].Name() } -func (d *TypeDiscriminator) matches(index int, t *Type) bool { +func (d *TypeDiscriminator) matches(index int, t *Type, isOptional bool) bool { + // Two optional discriminants always overlap through absence. Under strictNullChecks this is what the + // relation below would conclude anyway, so skip the property type resolution and the comparisons. + if isOptional && d.c.strictNullChecks && d.props[index].Flags()&ast.SymbolFlagsOptional != 0 { + return true + } sourceType, targetType := d.c.getTypeOfSymbol(d.props[index]), t if d.reverse { sourceType, targetType = targetType, sourceType @@ -1201,9 +1213,9 @@ func (c *Checker) mapTypesByKeyProperty(types []*Type, keyPropertyName string) m } type Discriminator interface { - len() int // Number of discriminant properties - name(index int) string // Property name of index-th discriminator - matches(index int, t *Type) bool // True if index-th discriminator matches the given type + len() int // Number of discriminant properties + name(index int) string // Property name of index-th discriminator + matches(index int, t *Type, isOptional bool) bool // True if index-th discriminator matches the given type, which is optional if isOptional is set } func (c *Checker) discriminateTypeByDiscriminableItems(target *Type, discriminator Discriminator) *Type { @@ -1221,9 +1233,17 @@ func (c *Checker) discriminateTypeByDiscriminableItems(target *Type, discriminat matched := false for i := range types { if include[i] != TernaryFalse { - targetType := c.getTypeOfPropertyOrIndexSignatureOfType(types[i], discriminator.name(n)) + name := discriminator.name(n) + var targetType *Type + isOptional := false + if prop := c.getPropertyOfType(types[i], name); prop != nil { + targetType = c.getTypeOfSymbol(prop) + isOptional = prop.Flags()&ast.SymbolFlagsOptional != 0 + } else if indexInfo := c.getApplicableIndexInfoForName(types[i], name); indexInfo != nil { + targetType = c.addOptionalityEx(indexInfo.valueType, true /*isProperty*/, true /*isOptional*/) + } if targetType != nil { - if discriminator.matches(n, targetType) { + if discriminator.matches(n, targetType, isOptional) { matched = true } else { include[i] = TernaryMaybe diff --git a/tsc/testdata/baselines/reference/compiler/inferenceDiscriminantOverlap(exactoptionalpropertytypes=false).errors.txt b/tsc/testdata/baselines/reference/compiler/inferenceDiscriminantOverlap(exactoptionalpropertytypes=false).errors.txt index 1b20b50e0a9b7..dc3fa84ee0773 100644 --- a/tsc/testdata/baselines/reference/compiler/inferenceDiscriminantOverlap(exactoptionalpropertytypes=false).errors.txt +++ b/tsc/testdata/baselines/reference/compiler/inferenceDiscriminantOverlap(exactoptionalpropertytypes=false).errors.txt @@ -35,13 +35,9 @@ inferenceDiscriminantOverlap.ts(39,5): error TS2322: Type '{ tag?: "a" | undefin Types of property 'tag' are incompatible. Type '"a" | undefined' is not assignable to type '"b" | undefined'. Type '"a"' is not assignable to type '"b"'. -inferenceDiscriminantOverlap.ts(55,5): error TS2322: Type '{ kind: "a"; value: readonly []; }' is not assignable to type '{ kind: string; value: string[]; } | { kind: "a"; value: number; }'. - Type '{ kind: "a"; value: readonly []; }' is not assignable to type '{ kind: "a"; value: number; }'. - Types of property 'value' are incompatible. - Type 'readonly []' is not assignable to type 'number'. -==== inferenceDiscriminantOverlap.ts (9 errors) ==== +==== inferenceDiscriminantOverlap.ts (8 errors) ==== declare const validOptionalSource: { tag: "a"; value: 1 } | { tag?: "a"; value: 2 }; declare const mixedOptionalSource: { tag: "a"; value: 1 } | { tag?: "b"; value: 2 }; declare const matchingOptionalSource: { tag: "a"; value: 1 } | { tag?: "a"; value: 2 } | { tag: "b"; value: 3 }; @@ -142,11 +138,6 @@ inferenceDiscriminantOverlap.ts(55,5): error TS2322: Type '{ kind: "a"; value: r export function broadContext(): { kind: string; value: string[] } | { kind: "a"; value: number } { return makeTagged([]); - ~~~~~~ -!!! error TS2322: Type '{ kind: "a"; value: readonly []; }' is not assignable to type '{ kind: string; value: string[]; } | { kind: "a"; value: number; }'. -!!! error TS2322: Type '{ kind: "a"; value: readonly []; }' is not assignable to type '{ kind: "a"; value: number; }'. -!!! error TS2322: Types of property 'value' are incompatible. -!!! error TS2322: Type 'readonly []' is not assignable to type 'number'. } export function partialTagContext(): { kind: "a" | "b"; value: string[] } | { kind: "a"; value: number } { return makeTagged([]); diff --git a/tsc/testdata/baselines/reference/compiler/inferenceDiscriminantOverlap(exactoptionalpropertytypes=false).types b/tsc/testdata/baselines/reference/compiler/inferenceDiscriminantOverlap(exactoptionalpropertytypes=false).types index e81aec1af831c..23ed13fce9665 100644 --- a/tsc/testdata/baselines/reference/compiler/inferenceDiscriminantOverlap(exactoptionalpropertytypes=false).types +++ b/tsc/testdata/baselines/reference/compiler/inferenceDiscriminantOverlap(exactoptionalpropertytypes=false).types @@ -257,7 +257,7 @@ export function broadContext(): { kind: string; value: string[] } | { kind: "a"; >value : number return makeTagged([]); ->makeTagged([]) : { kind: "a"; value: readonly []; } +>makeTagged([]) : { kind: "a"; value: []; } >makeTagged : (value: T) => { kind: "a"; value: T; } >[] : [] } diff --git a/tsc/testdata/baselines/reference/compiler/inferenceDiscriminantOverlap(exactoptionalpropertytypes=true).errors.txt b/tsc/testdata/baselines/reference/compiler/inferenceDiscriminantOverlap(exactoptionalpropertytypes=true).errors.txt index ae7def2eb27f3..e36038126dca7 100644 --- a/tsc/testdata/baselines/reference/compiler/inferenceDiscriminantOverlap(exactoptionalpropertytypes=true).errors.txt +++ b/tsc/testdata/baselines/reference/compiler/inferenceDiscriminantOverlap(exactoptionalpropertytypes=true).errors.txt @@ -35,10 +35,6 @@ inferenceDiscriminantOverlap.ts(39,5): error TS2375: Type '{ tag?: "a"; value: 1 Type '{ tag?: "a"; value: 1 | 2; }' is not assignable to type '{ tag?: "b"; value: 2; }' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties. Types of property 'tag' are incompatible. Type '"a"' is not assignable to type '"b"'. -inferenceDiscriminantOverlap.ts(55,5): error TS2322: Type '{ kind: "a"; value: readonly []; }' is not assignable to type '{ kind: string; value: string[]; } | { kind: "a"; value: number; }'. - Type '{ kind: "a"; value: readonly []; }' is not assignable to type '{ kind: "a"; value: number; }'. - Types of property 'value' are incompatible. - Type 'readonly []' is not assignable to type 'number'. inferenceDiscriminantOverlap.ts(61,5): error TS2375: Type '{ kind: "a" | undefined; value: []; }' is not assignable to type '{ kind?: "a"; value: string[]; } | { kind: "a"; value: number; }' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties. Type '{ kind: "a" | undefined; value: []; }' is not assignable to type '{ kind: "a"; value: number; }'. Types of property 'kind' are incompatible. @@ -46,7 +42,7 @@ inferenceDiscriminantOverlap.ts(61,5): error TS2375: Type '{ kind: "a" | undefin Type 'undefined' is not assignable to type '"a"'. -==== inferenceDiscriminantOverlap.ts (11 errors) ==== +==== inferenceDiscriminantOverlap.ts (10 errors) ==== declare const validOptionalSource: { tag: "a"; value: 1 } | { tag?: "a"; value: 2 }; declare const mixedOptionalSource: { tag: "a"; value: 1 } | { tag?: "b"; value: 2 }; declare const matchingOptionalSource: { tag: "a"; value: 1 } | { tag?: "a"; value: 2 } | { tag: "b"; value: 3 }; @@ -148,11 +144,6 @@ inferenceDiscriminantOverlap.ts(61,5): error TS2375: Type '{ kind: "a" | undefin export function broadContext(): { kind: string; value: string[] } | { kind: "a"; value: number } { return makeTagged([]); - ~~~~~~ -!!! error TS2322: Type '{ kind: "a"; value: readonly []; }' is not assignable to type '{ kind: string; value: string[]; } | { kind: "a"; value: number; }'. -!!! error TS2322: Type '{ kind: "a"; value: readonly []; }' is not assignable to type '{ kind: "a"; value: number; }'. -!!! error TS2322: Types of property 'value' are incompatible. -!!! error TS2322: Type 'readonly []' is not assignable to type 'number'. } export function partialTagContext(): { kind: "a" | "b"; value: string[] } | { kind: "a"; value: number } { return makeTagged([]); diff --git a/tsc/testdata/baselines/reference/compiler/inferenceDiscriminantOverlap(exactoptionalpropertytypes=true).types b/tsc/testdata/baselines/reference/compiler/inferenceDiscriminantOverlap(exactoptionalpropertytypes=true).types index 344407613a093..9302ce41820d2 100644 --- a/tsc/testdata/baselines/reference/compiler/inferenceDiscriminantOverlap(exactoptionalpropertytypes=true).types +++ b/tsc/testdata/baselines/reference/compiler/inferenceDiscriminantOverlap(exactoptionalpropertytypes=true).types @@ -257,7 +257,7 @@ export function broadContext(): { kind: string; value: string[] } | { kind: "a"; >value : number return makeTagged([]); ->makeTagged([]) : { kind: "a"; value: readonly []; } +>makeTagged([]) : { kind: "a"; value: []; } >makeTagged : (value: T) => { kind: "a"; value: T; } >[] : [] } diff --git a/tsc/testdata/baselines/reference/compiler/inferenceDiscriminatedUnions1(exactoptionalpropertytypes=false).errors.txt b/tsc/testdata/baselines/reference/compiler/inferenceDiscriminatedUnions1(exactoptionalpropertytypes=false).errors.txt index d8b167c95c2aa..991793bf5c43a 100644 --- a/tsc/testdata/baselines/reference/compiler/inferenceDiscriminatedUnions1(exactoptionalpropertytypes=false).errors.txt +++ b/tsc/testdata/baselines/reference/compiler/inferenceDiscriminatedUnions1(exactoptionalpropertytypes=false).errors.txt @@ -2,8 +2,8 @@ inferenceDiscriminatedUnions1.ts(52,17): error TS2345: Argument of type '{ kind: Type '{ kind: "b"; value: number; }' is not assignable to type '{ kind: "a"; value: string; }'. Types of property 'kind' are incompatible. Type '"b"' is not assignable to type '"a"'. -inferenceDiscriminatedUnions1.ts(53,17): error TS2345: Argument of type '{ kind: string; value: number; } | { kind: "a"; value: string; }' is not assignable to parameter of type '{ kind: "a"; value: string; }'. - Type '{ kind: string; value: number; }' is not assignable to type '{ kind: "a"; value: string; }'. +inferenceDiscriminatedUnions1.ts(53,17): error TS2345: Argument of type '{ kind: string; value: number; } | { kind: "a"; value: string; }' is not assignable to parameter of type '{ kind: "a"; value: number; }'. + Type '{ kind: string; value: number; }' is not assignable to type '{ kind: "a"; value: number; }'. Types of property 'kind' are incompatible. Type 'string' is not assignable to type '"a"'. inferenceDiscriminatedUnions1.ts(54,26): error TS2345: Argument of type '{ kind: number; value: number; } | { kind: "a"; value: string; }' is not assignable to parameter of type '{ kind: "a"; value: string; }'. @@ -121,8 +121,8 @@ inferenceDiscriminatedUnions1.ts(150,68): error TS2345: Argument of type '{ type !!! error TS2345: Type '"b"' is not assignable to type '"a"'. const r4 = pick(u2); ~~ -!!! error TS2345: Argument of type '{ kind: string; value: number; } | { kind: "a"; value: string; }' is not assignable to parameter of type '{ kind: "a"; value: string; }'. -!!! error TS2345: Type '{ kind: string; value: number; }' is not assignable to type '{ kind: "a"; value: string; }'. +!!! error TS2345: Argument of type '{ kind: string; value: number; } | { kind: "a"; value: string; }' is not assignable to parameter of type '{ kind: "a"; value: number; }'. +!!! error TS2345: Type '{ kind: string; value: number; }' is not assignable to type '{ kind: "a"; value: number; }'. !!! error TS2345: Types of property 'kind' are incompatible. !!! error TS2345: Type 'string' is not assignable to type '"a"'. const broadResult = pick(broadTag); diff --git a/tsc/testdata/baselines/reference/compiler/inferenceDiscriminatedUnions1(exactoptionalpropertytypes=false).types b/tsc/testdata/baselines/reference/compiler/inferenceDiscriminatedUnions1(exactoptionalpropertytypes=false).types index 062dd89c523e8..51233ebc8cdcc 100644 --- a/tsc/testdata/baselines/reference/compiler/inferenceDiscriminatedUnions1(exactoptionalpropertytypes=false).types +++ b/tsc/testdata/baselines/reference/compiler/inferenceDiscriminatedUnions1(exactoptionalpropertytypes=false).types @@ -252,8 +252,8 @@ const r3 = pick(u1); >u1 : { kind: "b"; value: number; } | { kind: "a"; value: string; } const r4 = pick(u2); ->r4 : string ->pick(u2) : string +>r4 : number +>pick(u2) : number >pick : (x: { kind: "a"; value: T; }) => T >u2 : { kind: string; value: number; } | { kind: "a"; value: string; } diff --git a/tsc/testdata/baselines/reference/compiler/inferenceDiscriminatedUnions1(exactoptionalpropertytypes=true).errors.txt b/tsc/testdata/baselines/reference/compiler/inferenceDiscriminatedUnions1(exactoptionalpropertytypes=true).errors.txt index cb2347d1ff027..f414a562f6319 100644 --- a/tsc/testdata/baselines/reference/compiler/inferenceDiscriminatedUnions1(exactoptionalpropertytypes=true).errors.txt +++ b/tsc/testdata/baselines/reference/compiler/inferenceDiscriminatedUnions1(exactoptionalpropertytypes=true).errors.txt @@ -2,8 +2,8 @@ inferenceDiscriminatedUnions1.ts(52,17): error TS2345: Argument of type '{ kind: Type '{ kind: "b"; value: number; }' is not assignable to type '{ kind: "a"; value: string; }'. Types of property 'kind' are incompatible. Type '"b"' is not assignable to type '"a"'. -inferenceDiscriminatedUnions1.ts(53,17): error TS2345: Argument of type '{ kind: string; value: number; } | { kind: "a"; value: string; }' is not assignable to parameter of type '{ kind: "a"; value: string; }'. - Type '{ kind: string; value: number; }' is not assignable to type '{ kind: "a"; value: string; }'. +inferenceDiscriminatedUnions1.ts(53,17): error TS2345: Argument of type '{ kind: string; value: number; } | { kind: "a"; value: string; }' is not assignable to parameter of type '{ kind: "a"; value: number; }'. + Type '{ kind: string; value: number; }' is not assignable to type '{ kind: "a"; value: number; }'. Types of property 'kind' are incompatible. Type 'string' is not assignable to type '"a"'. inferenceDiscriminatedUnions1.ts(54,26): error TS2345: Argument of type '{ kind: number; value: number; } | { kind: "a"; value: string; }' is not assignable to parameter of type '{ kind: "a"; value: string; }'. @@ -121,8 +121,8 @@ inferenceDiscriminatedUnions1.ts(150,68): error TS2345: Argument of type '{ type !!! error TS2345: Type '"b"' is not assignable to type '"a"'. const r4 = pick(u2); ~~ -!!! error TS2345: Argument of type '{ kind: string; value: number; } | { kind: "a"; value: string; }' is not assignable to parameter of type '{ kind: "a"; value: string; }'. -!!! error TS2345: Type '{ kind: string; value: number; }' is not assignable to type '{ kind: "a"; value: string; }'. +!!! error TS2345: Argument of type '{ kind: string; value: number; } | { kind: "a"; value: string; }' is not assignable to parameter of type '{ kind: "a"; value: number; }'. +!!! error TS2345: Type '{ kind: string; value: number; }' is not assignable to type '{ kind: "a"; value: number; }'. !!! error TS2345: Types of property 'kind' are incompatible. !!! error TS2345: Type 'string' is not assignable to type '"a"'. const broadResult = pick(broadTag); diff --git a/tsc/testdata/baselines/reference/compiler/inferenceDiscriminatedUnions1(exactoptionalpropertytypes=true).types b/tsc/testdata/baselines/reference/compiler/inferenceDiscriminatedUnions1(exactoptionalpropertytypes=true).types index 062dd89c523e8..51233ebc8cdcc 100644 --- a/tsc/testdata/baselines/reference/compiler/inferenceDiscriminatedUnions1(exactoptionalpropertytypes=true).types +++ b/tsc/testdata/baselines/reference/compiler/inferenceDiscriminatedUnions1(exactoptionalpropertytypes=true).types @@ -252,8 +252,8 @@ const r3 = pick(u1); >u1 : { kind: "b"; value: number; } | { kind: "a"; value: string; } const r4 = pick(u2); ->r4 : string ->pick(u2) : string +>r4 : number +>pick(u2) : number >pick : (x: { kind: "a"; value: T; }) => T >u2 : { kind: string; value: number; } | { kind: "a"; value: string; }