diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 33d2dd73876b8..83d257cd3d26b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -12485,7 +12485,7 @@ namespace ts { if (result && isPerformingExcessPropertyChecks) { // Validate against excess props using the original `source` const discriminantType = target.flags & TypeFlags.Union ? findMatchingDiscriminantType(source, target as UnionType) : undefined; - if (!propertiesRelatedTo(source, discriminantType || target, reportErrors)) { + if (!propertiesRelatedTo(source, discriminantType || target, reportErrors, /*excludedProperties*/ undefined)) { return Ternary.False; } } @@ -12495,7 +12495,7 @@ namespace ts { result = typeRelatedToEachType(getRegularTypeOfObjectLiteral(source), target as IntersectionType, reportErrors); if (result && isPerformingExcessPropertyChecks) { // Validate against excess props using the original `source` - if (!propertiesRelatedTo(source, target, reportErrors)) { + if (!propertiesRelatedTo(source, target, reportErrors, /*excludedProperties*/ undefined)) { return Ternary.False; } } @@ -13173,7 +13173,7 @@ namespace ts { if (source.flags & (TypeFlags.Object | TypeFlags.Intersection) && target.flags & TypeFlags.Object) { // Report structural errors only if we haven't reported any errors yet const reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo && !sourceIsPrimitive; - result = propertiesRelatedTo(source, target, reportStructuralErrors); + result = propertiesRelatedTo(source, target, reportStructuralErrors, /*excludedProperties*/ undefined); if (result) { result &= signaturesRelatedTo(source, target, SignatureKind.Call, reportStructuralErrors); if (result) { @@ -13193,6 +13193,19 @@ namespace ts { return result; } } + // If S is an object type and T is a discriminated union, S may be related to T if + // there exists a constituent of T for every combination of the discriminants of S + // with respect to T. We do not report errors here, as we will use the existing + // error result from checking each constituent of the union. + if (source.flags & (TypeFlags.Object | TypeFlags.Intersection) && target.flags & TypeFlags.Union) { + const objectOnlyTarget = extractTypesOfKind(target, TypeFlags.Object); + if (objectOnlyTarget.flags & TypeFlags.Union) { + const result = typeRelatedToDiscriminatedType(source, objectOnlyTarget as UnionType); + if (result) { + return result; + } + } + } } return Ternary.False; @@ -13256,9 +13269,185 @@ namespace ts { return Ternary.False; } - function propertiesRelatedTo(source: Type, target: Type, reportErrors: boolean): Ternary { + function typeRelatedToDiscriminatedType(source: Type, target: UnionType) { + // 1. Generate the combinations of discriminant properties & types 'source' can satisfy. + // a. If the number of combinations is above a set limit, the comparison is too complex. + // 2. Filter 'target' to the subset of types whose discriminants exist in the matrix. + // a. If 'target' does not satisfy all discriminants in the matrix, 'source' is not related. + // 3. For each type in the filtered 'target', determine if all non-discriminant properties of + // 'target' are related to a property in 'source'. + // + // NOTE: See ~/tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithDiscriminatedUnion.ts + // for examples. + + const sourceProperties = getPropertiesOfObjectType(source); + const sourcePropertiesFiltered = findDiscriminantProperties(sourceProperties, target); + if (!sourcePropertiesFiltered) return Ternary.False; + + // Though we could compute the number of combinations as we generate + // the matrix, this would incur additional memory overhead due to + // array allocations. To reduce this overhead, we first compute + // the number of combinations to ensure we will not surpass our + // fixed limit before incurring the cost of any allocations: + let numCombinations = 1; + for (const sourceProperty of sourcePropertiesFiltered) { + numCombinations *= countTypes(getTypeOfSymbol(sourceProperty)); + if (numCombinations > 25) { + // We've reached the complexity limit. + return Ternary.False; + } + } + + // Compute the set of types for each discriminant property. + const sourceDiscriminantTypes: Type[][] = new Array(sourcePropertiesFiltered.length); + const excludedProperties = createUnderscoreEscapedMap(); + for (let i = 0; i < sourcePropertiesFiltered.length; i++) { + const sourceProperty = sourcePropertiesFiltered[i]; + const sourcePropertyType = getTypeOfSymbol(sourceProperty); + sourceDiscriminantTypes[i] = sourcePropertyType.flags & TypeFlags.Union + ? (sourcePropertyType as UnionType).types + : [sourcePropertyType]; + excludedProperties.set(sourceProperty.escapedName, true); + } + + // Match each combination of the cartesian product of discriminant properties to one or more + // constituents of 'target'. If any combination does not have a match then 'source' is not relatable. + const discriminantCombinations = cartesianProduct(sourceDiscriminantTypes); + const matchingTypes: Type[] = []; + for (const combination of discriminantCombinations) { + let hasMatch = false; + outer: for (const type of target.types) { + for (let i = 0; i < sourcePropertiesFiltered.length; i++) { + const sourceProperty = sourcePropertiesFiltered[i]; + const targetProperty = getPropertyOfObjectType(type, sourceProperty.escapedName); + if (!targetProperty) continue outer; + if (sourceProperty === targetProperty) continue; + // We compare the source property to the target in the context of a single discriminant type. + const related = propertyRelatedTo(source, target, sourceProperty, targetProperty, _ => combination[i], /*reportErrors*/ false); + // If the target property could not be found, or if the properties were not related, + // then this constituent is not a match. + if (!related) { + continue outer; + } + } + pushIfUnique(matchingTypes, type, equateValues); + hasMatch = true; + } + if (!hasMatch) { + // We failed to match any type for this combination. + return Ternary.False; + } + } + + // Compare the remaining non-discriminant properties of each match. + let result = Ternary.True; + for (const type of matchingTypes) { + result &= propertiesRelatedTo(source, type, /*reportErrors*/ false, excludedProperties); + if (result) { + result &= signaturesRelatedTo(source, type, SignatureKind.Call, /*reportStructuralErrors*/ false); + if (result) { + result &= signaturesRelatedTo(source, type, SignatureKind.Construct, /*reportStructuralErrors*/ false); + if (result) { + result &= indexTypesRelatedTo(source, type, IndexKind.String, /*sourceIsPrimitive*/ false, /*reportStructuralErrors*/ false); + if (result) { + result &= indexTypesRelatedTo(source, type, IndexKind.Number, /*sourceIsPrimitive*/ false, /*reportStructuralErrors*/ false); + } + } + } + } + if (!result) { + return result; + } + } + return result; + } + + function excludeProperties(properties: Symbol[], excludedProperties: UnderscoreEscapedMap | undefined) { + if (!excludedProperties || properties.length === 0) return properties; + let result: Symbol[] | undefined; + for (let i = 0; i < properties.length; i++) { + if (!excludedProperties.has(properties[i].escapedName)) { + if (result) { + result.push(properties[i]); + } + } + else if (!result) { + result = properties.slice(0, i); + } + } + return result || properties; + } + + function propertyRelatedTo(source: Type, target: Type, sourceProp: Symbol, targetProp: Symbol, getTypeOfSourceProperty: (sym: Symbol) => Type, reportErrors: boolean): Ternary { + const sourcePropFlags = getDeclarationModifierFlagsFromSymbol(sourceProp); + const targetPropFlags = getDeclarationModifierFlagsFromSymbol(targetProp); + if (sourcePropFlags & ModifierFlags.Private || targetPropFlags & ModifierFlags.Private) { + const hasDifferingDeclarations = sourceProp.valueDeclaration !== targetProp.valueDeclaration; + if (getCheckFlags(sourceProp) & CheckFlags.ContainsPrivate && hasDifferingDeclarations) { + if (reportErrors) { + reportError(Diagnostics.Property_0_has_conflicting_declarations_and_is_inaccessible_in_type_1, symbolToString(sourceProp), typeToString(source)); + } + return Ternary.False; + } + if (hasDifferingDeclarations) { + if (reportErrors) { + if (sourcePropFlags & ModifierFlags.Private && targetPropFlags & ModifierFlags.Private) { + reportError(Diagnostics.Types_have_separate_declarations_of_a_private_property_0, symbolToString(targetProp)); + } + else { + reportError(Diagnostics.Property_0_is_private_in_type_1_but_not_in_type_2, symbolToString(targetProp), + typeToString(sourcePropFlags & ModifierFlags.Private ? source : target), + typeToString(sourcePropFlags & ModifierFlags.Private ? target : source)); + } + } + return Ternary.False; + } + } + else if (targetPropFlags & ModifierFlags.Protected) { + if (!isValidOverrideOf(sourceProp, targetProp)) { + if (reportErrors) { + reportError(Diagnostics.Property_0_is_protected_but_type_1_is_not_a_class_derived_from_2, symbolToString(targetProp), + typeToString(getDeclaringClass(sourceProp) || source), typeToString(getDeclaringClass(targetProp) || target)); + } + return Ternary.False; + } + } + else if (sourcePropFlags & ModifierFlags.Protected) { + if (reportErrors) { + reportError(Diagnostics.Property_0_is_protected_in_type_1_but_public_in_type_2, + symbolToString(targetProp), typeToString(source), typeToString(target)); + } + return Ternary.False; + } + // If the target comes from a partial union prop, allow `undefined` in the target type + const related = isRelatedTo(getTypeOfSourceProperty(sourceProp), addOptionality(getTypeOfSymbol(targetProp), !!(getCheckFlags(targetProp) & CheckFlags.Partial)), reportErrors); + if (!related) { + if (reportErrors) { + reportError(Diagnostics.Types_of_property_0_are_incompatible, symbolToString(targetProp)); + } + return Ternary.False; + } + // When checking for comparability, be more lenient with optional properties. + if (relation !== comparableRelation && sourceProp.flags & SymbolFlags.Optional && !(targetProp.flags & SymbolFlags.Optional)) { + // TypeScript 1.0 spec (April 2014): 3.8.3 + // S is a subtype of a type T, and T is a supertype of S if ... + // S' and T are object types and, for each member M in T.. + // M is a property and S' contains a property N where + // if M is a required property, N is also a required property + // (M - property in T) + // (N - property in S) + if (reportErrors) { + reportError(Diagnostics.Property_0_is_optional_in_type_1_but_required_in_type_2, + symbolToString(targetProp), typeToString(source), typeToString(target)); + } + return Ternary.False; + } + return related; + } + + function propertiesRelatedTo(source: Type, target: Type, reportErrors: boolean, excludedProperties: UnderscoreEscapedMap | undefined): Ternary { if (relation === identityRelation) { - return propertiesIdenticalTo(source, target); + return propertiesIdenticalTo(source, target, excludedProperties); } const requireOptionalProperties = relation === subtypeRelation && !isObjectLiteralType(source) && !isEmptyArrayLiteralType(source) && !isTupleType(source); const unmatchedProperty = getUnmatchedProperty(source, target, requireOptionalProperties, /*matchDiscriminantProperties*/ false); @@ -13288,7 +13477,7 @@ namespace ts { return Ternary.False; } if (isObjectLiteralType(target)) { - for (const sourceProp of getPropertiesOfType(source)) { + for (const sourceProp of excludeProperties(getPropertiesOfType(source), excludedProperties)) { if (!getPropertyOfObjectType(target, sourceProp.escapedName)) { const sourceType = getTypeOfSymbol(sourceProp); if (!(sourceType === undefinedType || sourceType === undefinedWideningType)) { @@ -13331,89 +13520,30 @@ namespace ts { // We only call this for union target types when we're attempting to do excess property checking - in those cases, we want to get _all possible props_ // from the target union, across all members const properties = target.flags & TypeFlags.Union ? getPossiblePropertiesOfUnionType(target as UnionType) : getPropertiesOfType(target); - for (const targetProp of properties) { + for (const targetProp of excludeProperties(properties, excludedProperties)) { if (!(targetProp.flags & SymbolFlags.Prototype)) { const sourceProp = getPropertyOfType(source, targetProp.escapedName); if (sourceProp && sourceProp !== targetProp) { if (isIgnoredJsxProperty(source, sourceProp, getTypeOfSymbol(targetProp))) { continue; } - const sourcePropFlags = getDeclarationModifierFlagsFromSymbol(sourceProp); - const targetPropFlags = getDeclarationModifierFlagsFromSymbol(targetProp); - if (sourcePropFlags & ModifierFlags.Private || targetPropFlags & ModifierFlags.Private) { - const hasDifferingDeclarations = sourceProp.valueDeclaration !== targetProp.valueDeclaration; - if (getCheckFlags(sourceProp) & CheckFlags.ContainsPrivate && hasDifferingDeclarations) { - if (reportErrors) { - reportError(Diagnostics.Property_0_has_conflicting_declarations_and_is_inaccessible_in_type_1, symbolToString(sourceProp), typeToString(source)); - } - return Ternary.False; - } - if (hasDifferingDeclarations) { - if (reportErrors) { - if (sourcePropFlags & ModifierFlags.Private && targetPropFlags & ModifierFlags.Private) { - reportError(Diagnostics.Types_have_separate_declarations_of_a_private_property_0, symbolToString(targetProp)); - } - else { - reportError(Diagnostics.Property_0_is_private_in_type_1_but_not_in_type_2, symbolToString(targetProp), - typeToString(sourcePropFlags & ModifierFlags.Private ? source : target), - typeToString(sourcePropFlags & ModifierFlags.Private ? target : source)); - } - } - return Ternary.False; - } - } - else if (targetPropFlags & ModifierFlags.Protected) { - if (!isValidOverrideOf(sourceProp, targetProp)) { - if (reportErrors) { - reportError(Diagnostics.Property_0_is_protected_but_type_1_is_not_a_class_derived_from_2, symbolToString(targetProp), - typeToString(getDeclaringClass(sourceProp) || source), typeToString(getDeclaringClass(targetProp) || target)); - } - return Ternary.False; - } - } - else if (sourcePropFlags & ModifierFlags.Protected) { - if (reportErrors) { - reportError(Diagnostics.Property_0_is_protected_in_type_1_but_public_in_type_2, - symbolToString(targetProp), typeToString(source), typeToString(target)); - } - return Ternary.False; - } - // If the target comes from a partial union prop, allow `undefined` in the target type - const related = isRelatedTo(getTypeOfSymbol(sourceProp), addOptionality(getTypeOfSymbol(targetProp), !!(getCheckFlags(targetProp) & CheckFlags.Partial)), reportErrors); + const related = propertyRelatedTo(source, target, sourceProp, targetProp, getTypeOfSymbol, reportErrors); if (!related) { - if (reportErrors) { - reportError(Diagnostics.Types_of_property_0_are_incompatible, symbolToString(targetProp)); - } return Ternary.False; } result &= related; - // When checking for comparability, be more lenient with optional properties. - if (relation !== comparableRelation && sourceProp.flags & SymbolFlags.Optional && !(targetProp.flags & SymbolFlags.Optional)) { - // TypeScript 1.0 spec (April 2014): 3.8.3 - // S is a subtype of a type T, and T is a supertype of S if ... - // S' and T are object types and, for each member M in T.. - // M is a property and S' contains a property N where - // if M is a required property, N is also a required property - // (M - property in T) - // (N - property in S) - if (reportErrors) { - reportError(Diagnostics.Property_0_is_optional_in_type_1_but_required_in_type_2, - symbolToString(targetProp), typeToString(source), typeToString(target)); - } - return Ternary.False; - } } } } return result; } - function propertiesIdenticalTo(source: Type, target: Type): Ternary { + function propertiesIdenticalTo(source: Type, target: Type, excludedProperties: UnderscoreEscapedMap | undefined): Ternary { if (!(source.flags & TypeFlags.Object && target.flags & TypeFlags.Object)) { return Ternary.False; } - const sourceProperties = getPropertiesOfObjectType(source); - const targetProperties = getPropertiesOfObjectType(target); + const sourceProperties = excludeProperties(getPropertiesOfObjectType(source), excludedProperties); + const targetProperties = excludeProperties(getPropertiesOfObjectType(target), excludedProperties); if (sourceProperties.length !== targetProperties.length) { return Ternary.False; } @@ -15952,6 +16082,10 @@ namespace ts { return f(type) ? type : neverType; } + function countTypes(type: Type) { + return type.flags & TypeFlags.Union ? (type as UnionType).types.length : 1; + } + // Apply a mapping function to a type and return the resulting type. If the source type // is a union type, the mapping function is applied to each constituent type and a union // of the resulting types is returned. diff --git a/src/compiler/core.ts b/src/compiler/core.ts index b3839842d3e60..8d2f01f3de27a 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -2284,4 +2284,29 @@ namespace ts { } return result; } + + export function cartesianProduct(arrays: readonly T[][]) { + const result: T[][] = []; + cartesianProductWorker(arrays, result, /*outer*/ undefined, 0); + return result; + } + + function cartesianProductWorker(arrays: readonly (readonly T[])[], result: (readonly T[])[], outer: readonly T[] | undefined, index: number) { + for (const element of arrays[index]) { + let inner: T[]; + if (outer) { + inner = outer.slice(); + inner.push(element); + } + else { + inner = [element]; + } + if (index === arrays.length - 1) { + result.push(inner); + } + else { + cartesianProductWorker(arrays, result, inner, index + 1); + } + } + } } diff --git a/tests/baselines/reference/assignmentCompatWithDiscriminatedUnion.errors.txt b/tests/baselines/reference/assignmentCompatWithDiscriminatedUnion.errors.txt new file mode 100644 index 0000000000000..e5e3ee8b49992 --- /dev/null +++ b/tests/baselines/reference/assignmentCompatWithDiscriminatedUnion.errors.txt @@ -0,0 +1,224 @@ +tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithDiscriminatedUnion.ts(44,5): error TS2322: Type 'S' is not assignable to type 'T'. + Type 'S' is not assignable to type '{ a: 2; b: 3; }'. + Types of property 'a' are incompatible. + Type '0 | 2' is not assignable to type '2'. + Type '0' is not assignable to type '2'. +tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithDiscriminatedUnion.ts(58,5): error TS2322: Type 'S' is not assignable to type 'T'. + Property 'c' is missing in type 'S' but required in type '{ a: 2; b: 4 | 3; c: string; }'. +tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithDiscriminatedUnion.ts(82,5): error TS2322: Type 'S' is not assignable to type 'T'. + Type 'S' is not assignable to type '{ a: 0 | 2 | 1; b: 0 | 2 | 1; c: 2; }'. + Types of property 'c' are incompatible. + Type '0 | 2 | 1' is not assignable to type '2'. + Type '0' is not assignable to type '2'. + + +==== tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithDiscriminatedUnion.ts (3 errors) ==== + // see 'typeRelatedToDiscriminatedType' in checker.ts: + + // IteratorResult + namespace Example1 { + type S = { done: boolean, value: number }; + type T = + | { done: true, value: number } // T0 + | { done: false, value: number }; // T1 + + declare let s: S; + declare let t: T; + + // S is assignable to T0 when S["done"] is true + // S is assignable to T1 when S["done"] is false + t = s; + } + + // Dropping constituents of T + namespace Example2 { + type S = { a: 0 | 2, b: 4 }; + type T = { a: 0, b: 1 | 4 } // T0 + | { a: 1, b: 2 } // T1 + | { a: 2, b: 3 | 4 }; // T2 + declare let s: S; + declare let t: T; + + // S is assignable to T0 when S["a"] is 0 + // S is assignable to T2 when S["a"] is 2 + t = s; + } + + // Unmatched discriminants + namespace Example3 { + type S = { a: 0 | 2, b: 4 }; + type T = { a: 0, b: 1 | 4 } // T0 + | { a: 1, b: 2 | 4 } // T1 + | { a: 2, b: 3 }; // T2 + declare let s: S; + declare let t: T; + + // S is assignable to T0 when S["a"] is 0 + // S is *not* assignable to T1 when S["b"] is 4 + // S is *not* assignable to T2 when S["a"] is 2 + t = s; + ~ +!!! error TS2322: Type 'S' is not assignable to type 'T'. +!!! error TS2322: Type 'S' is not assignable to type '{ a: 2; b: 3; }'. +!!! error TS2322: Types of property 'a' are incompatible. +!!! error TS2322: Type '0 | 2' is not assignable to type '2'. +!!! error TS2322: Type '0' is not assignable to type '2'. + } + + // Unmatched non-discriminants + namespace Example4 { + type S = { a: 0 | 2, b: 4 }; + type T = { a: 0, b: 1 | 4 } // T0 + | { a: 1, b: 2 } // T1 + | { a: 2, b: 3 | 4, c: string }; // T2 + declare let s: S; + declare let t: T; + + // S is assignable to T0 when S["a"] is 0 + // S is *not* assignable to T2 when S["a"] is 2 as S is missing "c" + t = s; + ~ +!!! error TS2322: Type 'S' is not assignable to type 'T'. +!!! error TS2322: Property 'c' is missing in type 'S' but required in type '{ a: 2; b: 4 | 3; c: string; }'. +!!! related TS2728 tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithDiscriminatedUnion.ts:52:36: 'c' is declared here. + } + + // Maximum discriminant combinations + namespace Example5 { + // NOTE: The maximum number of discriminant type combinations is currently 25. + // 3 discriminant properties with 3 types a piece + // is 27 possible combinations. + type N = 0 | 1 | 2; + type S = { a: N, b: N, c: N }; + type T = { a: 0, b: N, c: N } + | { a: 1, b: N, c: N } + | { a: 2, b: N, c: N } + | { a: N, b: 0, c: N } + | { a: N, b: 1, c: N } + | { a: N, b: 2, c: N } + | { a: N, b: N, c: 0 } + | { a: N, b: N, c: 1 } + | { a: N, b: N, c: 2 }; + declare let s: S; + declare let t: T; + + // S *should* be assignable but the number of + // combinations is too complex. + t = s; + ~ +!!! error TS2322: Type 'S' is not assignable to type 'T'. +!!! error TS2322: Type 'S' is not assignable to type '{ a: 0 | 2 | 1; b: 0 | 2 | 1; c: 2; }'. +!!! error TS2322: Types of property 'c' are incompatible. +!!! error TS2322: Type '0 | 2 | 1' is not assignable to type '2'. +!!! error TS2322: Type '0' is not assignable to type '2'. + } + + // https://github.com/Microsoft/TypeScript/issues/14865 + namespace GH14865 { + type Style1 = { + type: "A"; + data: string; + } | { + type: "B"; + data: string; + }; + + type Style2 = { + type: "A" | "B"; + data: string; + } + + const a: Style2 = { type: "A", data: "whatevs" }; + let b: Style1; + a.type; // "A" | "B" + b.type; // "A" | "B" + b = a; // should be assignable + } + + // https://github.com/Microsoft/TypeScript/issues/30170 + namespace GH30170 { + interface Blue { + color: 'blue' + } + interface Yellow { + color?: 'yellow', + } + function draw(val: Blue | Yellow) { } + + function drawWithColor(currentColor: 'blue' | 'yellow' | undefined) { + return draw({ color: currentColor }); + } + } + + // https://github.com/Microsoft/TypeScript/issues/12052 + namespace GH12052 { + interface ILinearAxis { type: "linear"; } + + interface ICategoricalAxis { type: "categorical"; } + + type IAxis = ILinearAxis | ICategoricalAxis; + type IAxisType = "linear" | "categorical"; + + function getAxisType(): IAxisType { + if (1 == 1) { + return "categorical"; + } else { + return "linear"; + } + } + + const bad: IAxis = { type: getAxisType() }; + const good: IAxis = { type: undefined }; + good.type = getAxisType(); + } + + // https://github.com/Microsoft/TypeScript/issues/18421 + namespace GH18421 { + interface ThingTypeOne { + type: 'one'; + } + + interface ThingTypeTwo { + type: 'two'; + } + + type ThingType = 'one' | 'two'; + + type Thing = ThingTypeOne | ThingTypeTwo; + + function makeNewThing(thingType: ThingType): Thing { + return { + type: thingType + }; + } + } + + // https://github.com/Microsoft/TypeScript/issues/15907 + namespace GH15907 { + type Action = { type: 'activate' } | { type: 'disactivate' }; + + function dispatchAction(action: Action): void { + + } + + const active = true; + + dispatchAction({ type : (active? 'disactivate' : 'activate') }); + } + + // https://github.com/Microsoft/TypeScript/issues/20889 + namespace GH20889 { + interface A1 { + type: "A1"; + } + interface A2 { + type: "A2"; + } + type AU = A1 | A2; + + function foo(obj1: AU) { + const obj2: AU = { + type: obj1.type + }; + } + } \ No newline at end of file diff --git a/tests/baselines/reference/assignmentCompatWithDiscriminatedUnion.js b/tests/baselines/reference/assignmentCompatWithDiscriminatedUnion.js new file mode 100644 index 0000000000000..2719a525ba7a5 --- /dev/null +++ b/tests/baselines/reference/assignmentCompatWithDiscriminatedUnion.js @@ -0,0 +1,291 @@ +//// [assignmentCompatWithDiscriminatedUnion.ts] +// see 'typeRelatedToDiscriminatedType' in checker.ts: + +// IteratorResult +namespace Example1 { + type S = { done: boolean, value: number }; + type T = + | { done: true, value: number } // T0 + | { done: false, value: number }; // T1 + + declare let s: S; + declare let t: T; + + // S is assignable to T0 when S["done"] is true + // S is assignable to T1 when S["done"] is false + t = s; +} + +// Dropping constituents of T +namespace Example2 { + type S = { a: 0 | 2, b: 4 }; + type T = { a: 0, b: 1 | 4 } // T0 + | { a: 1, b: 2 } // T1 + | { a: 2, b: 3 | 4 }; // T2 + declare let s: S; + declare let t: T; + + // S is assignable to T0 when S["a"] is 0 + // S is assignable to T2 when S["a"] is 2 + t = s; +} + +// Unmatched discriminants +namespace Example3 { + type S = { a: 0 | 2, b: 4 }; + type T = { a: 0, b: 1 | 4 } // T0 + | { a: 1, b: 2 | 4 } // T1 + | { a: 2, b: 3 }; // T2 + declare let s: S; + declare let t: T; + + // S is assignable to T0 when S["a"] is 0 + // S is *not* assignable to T1 when S["b"] is 4 + // S is *not* assignable to T2 when S["a"] is 2 + t = s; +} + +// Unmatched non-discriminants +namespace Example4 { + type S = { a: 0 | 2, b: 4 }; + type T = { a: 0, b: 1 | 4 } // T0 + | { a: 1, b: 2 } // T1 + | { a: 2, b: 3 | 4, c: string }; // T2 + declare let s: S; + declare let t: T; + + // S is assignable to T0 when S["a"] is 0 + // S is *not* assignable to T2 when S["a"] is 2 as S is missing "c" + t = s; +} + +// Maximum discriminant combinations +namespace Example5 { + // NOTE: The maximum number of discriminant type combinations is currently 25. + // 3 discriminant properties with 3 types a piece + // is 27 possible combinations. + type N = 0 | 1 | 2; + type S = { a: N, b: N, c: N }; + type T = { a: 0, b: N, c: N } + | { a: 1, b: N, c: N } + | { a: 2, b: N, c: N } + | { a: N, b: 0, c: N } + | { a: N, b: 1, c: N } + | { a: N, b: 2, c: N } + | { a: N, b: N, c: 0 } + | { a: N, b: N, c: 1 } + | { a: N, b: N, c: 2 }; + declare let s: S; + declare let t: T; + + // S *should* be assignable but the number of + // combinations is too complex. + t = s; +} + +// https://github.com/Microsoft/TypeScript/issues/14865 +namespace GH14865 { + type Style1 = { + type: "A"; + data: string; + } | { + type: "B"; + data: string; + }; + + type Style2 = { + type: "A" | "B"; + data: string; + } + + const a: Style2 = { type: "A", data: "whatevs" }; + let b: Style1; + a.type; // "A" | "B" + b.type; // "A" | "B" + b = a; // should be assignable +} + +// https://github.com/Microsoft/TypeScript/issues/30170 +namespace GH30170 { + interface Blue { + color: 'blue' + } + interface Yellow { + color?: 'yellow', + } + function draw(val: Blue | Yellow) { } + + function drawWithColor(currentColor: 'blue' | 'yellow' | undefined) { + return draw({ color: currentColor }); + } +} + +// https://github.com/Microsoft/TypeScript/issues/12052 +namespace GH12052 { + interface ILinearAxis { type: "linear"; } + + interface ICategoricalAxis { type: "categorical"; } + + type IAxis = ILinearAxis | ICategoricalAxis; + type IAxisType = "linear" | "categorical"; + + function getAxisType(): IAxisType { + if (1 == 1) { + return "categorical"; + } else { + return "linear"; + } + } + + const bad: IAxis = { type: getAxisType() }; + const good: IAxis = { type: undefined }; + good.type = getAxisType(); +} + +// https://github.com/Microsoft/TypeScript/issues/18421 +namespace GH18421 { + interface ThingTypeOne { + type: 'one'; + } + + interface ThingTypeTwo { + type: 'two'; + } + + type ThingType = 'one' | 'two'; + + type Thing = ThingTypeOne | ThingTypeTwo; + + function makeNewThing(thingType: ThingType): Thing { + return { + type: thingType + }; + } +} + +// https://github.com/Microsoft/TypeScript/issues/15907 +namespace GH15907 { + type Action = { type: 'activate' } | { type: 'disactivate' }; + + function dispatchAction(action: Action): void { + + } + + const active = true; + + dispatchAction({ type : (active? 'disactivate' : 'activate') }); +} + +// https://github.com/Microsoft/TypeScript/issues/20889 +namespace GH20889 { + interface A1 { + type: "A1"; + } + interface A2 { + type: "A2"; + } + type AU = A1 | A2; + + function foo(obj1: AU) { + const obj2: AU = { + type: obj1.type + }; + } +} + +//// [assignmentCompatWithDiscriminatedUnion.js] +// see 'typeRelatedToDiscriminatedType' in checker.ts: +// IteratorResult +var Example1; +(function (Example1) { + // S is assignable to T0 when S["done"] is true + // S is assignable to T1 when S["done"] is false + t = s; +})(Example1 || (Example1 = {})); +// Dropping constituents of T +var Example2; +(function (Example2) { + // S is assignable to T0 when S["a"] is 0 + // S is assignable to T2 when S["a"] is 2 + t = s; +})(Example2 || (Example2 = {})); +// Unmatched discriminants +var Example3; +(function (Example3) { + // S is assignable to T0 when S["a"] is 0 + // S is *not* assignable to T1 when S["b"] is 4 + // S is *not* assignable to T2 when S["a"] is 2 + t = s; +})(Example3 || (Example3 = {})); +// Unmatched non-discriminants +var Example4; +(function (Example4) { + // S is assignable to T0 when S["a"] is 0 + // S is *not* assignable to T2 when S["a"] is 2 as S is missing "c" + t = s; +})(Example4 || (Example4 = {})); +// Maximum discriminant combinations +var Example5; +(function (Example5) { + // S *should* be assignable but the number of + // combinations is too complex. + t = s; +})(Example5 || (Example5 = {})); +// https://github.com/Microsoft/TypeScript/issues/14865 +var GH14865; +(function (GH14865) { + var a = { type: "A", data: "whatevs" }; + var b; + a.type; // "A" | "B" + b.type; // "A" | "B" + b = a; // should be assignable +})(GH14865 || (GH14865 = {})); +// https://github.com/Microsoft/TypeScript/issues/30170 +var GH30170; +(function (GH30170) { + function draw(val) { } + function drawWithColor(currentColor) { + return draw({ color: currentColor }); + } +})(GH30170 || (GH30170 = {})); +// https://github.com/Microsoft/TypeScript/issues/12052 +var GH12052; +(function (GH12052) { + function getAxisType() { + if (1 == 1) { + return "categorical"; + } + else { + return "linear"; + } + } + var bad = { type: getAxisType() }; + var good = { type: undefined }; + good.type = getAxisType(); +})(GH12052 || (GH12052 = {})); +// https://github.com/Microsoft/TypeScript/issues/18421 +var GH18421; +(function (GH18421) { + function makeNewThing(thingType) { + return { + type: thingType + }; + } +})(GH18421 || (GH18421 = {})); +// https://github.com/Microsoft/TypeScript/issues/15907 +var GH15907; +(function (GH15907) { + function dispatchAction(action) { + } + var active = true; + dispatchAction({ type: (active ? 'disactivate' : 'activate') }); +})(GH15907 || (GH15907 = {})); +// https://github.com/Microsoft/TypeScript/issues/20889 +var GH20889; +(function (GH20889) { + function foo(obj1) { + var obj2 = { + type: obj1.type + }; + } +})(GH20889 || (GH20889 = {})); diff --git a/tests/baselines/reference/assignmentCompatWithDiscriminatedUnion.symbols b/tests/baselines/reference/assignmentCompatWithDiscriminatedUnion.symbols new file mode 100644 index 0000000000000..1d0afd3fd91dc --- /dev/null +++ b/tests/baselines/reference/assignmentCompatWithDiscriminatedUnion.symbols @@ -0,0 +1,494 @@ +=== tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithDiscriminatedUnion.ts === +// see 'typeRelatedToDiscriminatedType' in checker.ts: + +// IteratorResult +namespace Example1 { +>Example1 : Symbol(Example1, Decl(assignmentCompatWithDiscriminatedUnion.ts, 0, 0)) + + type S = { done: boolean, value: number }; +>S : Symbol(S, Decl(assignmentCompatWithDiscriminatedUnion.ts, 3, 20)) +>done : Symbol(done, Decl(assignmentCompatWithDiscriminatedUnion.ts, 4, 14)) +>value : Symbol(value, Decl(assignmentCompatWithDiscriminatedUnion.ts, 4, 29)) + + type T = +>T : Symbol(T, Decl(assignmentCompatWithDiscriminatedUnion.ts, 4, 46)) + + | { done: true, value: number } // T0 +>done : Symbol(done, Decl(assignmentCompatWithDiscriminatedUnion.ts, 6, 11)) +>value : Symbol(value, Decl(assignmentCompatWithDiscriminatedUnion.ts, 6, 23)) + + | { done: false, value: number }; // T1 +>done : Symbol(done, Decl(assignmentCompatWithDiscriminatedUnion.ts, 7, 11)) +>value : Symbol(value, Decl(assignmentCompatWithDiscriminatedUnion.ts, 7, 24)) + + declare let s: S; +>s : Symbol(s, Decl(assignmentCompatWithDiscriminatedUnion.ts, 9, 15)) +>S : Symbol(S, Decl(assignmentCompatWithDiscriminatedUnion.ts, 3, 20)) + + declare let t: T; +>t : Symbol(t, Decl(assignmentCompatWithDiscriminatedUnion.ts, 10, 15)) +>T : Symbol(T, Decl(assignmentCompatWithDiscriminatedUnion.ts, 4, 46)) + + // S is assignable to T0 when S["done"] is true + // S is assignable to T1 when S["done"] is false + t = s; +>t : Symbol(t, Decl(assignmentCompatWithDiscriminatedUnion.ts, 10, 15)) +>s : Symbol(s, Decl(assignmentCompatWithDiscriminatedUnion.ts, 9, 15)) +} + +// Dropping constituents of T +namespace Example2 { +>Example2 : Symbol(Example2, Decl(assignmentCompatWithDiscriminatedUnion.ts, 15, 1)) + + type S = { a: 0 | 2, b: 4 }; +>S : Symbol(S, Decl(assignmentCompatWithDiscriminatedUnion.ts, 18, 20)) +>a : Symbol(a, Decl(assignmentCompatWithDiscriminatedUnion.ts, 19, 14)) +>b : Symbol(b, Decl(assignmentCompatWithDiscriminatedUnion.ts, 19, 24)) + + type T = { a: 0, b: 1 | 4 } // T0 +>T : Symbol(T, Decl(assignmentCompatWithDiscriminatedUnion.ts, 19, 32)) +>a : Symbol(a, Decl(assignmentCompatWithDiscriminatedUnion.ts, 20, 14)) +>b : Symbol(b, Decl(assignmentCompatWithDiscriminatedUnion.ts, 20, 20)) + + | { a: 1, b: 2 } // T1 +>a : Symbol(a, Decl(assignmentCompatWithDiscriminatedUnion.ts, 21, 14)) +>b : Symbol(b, Decl(assignmentCompatWithDiscriminatedUnion.ts, 21, 20)) + + | { a: 2, b: 3 | 4 }; // T2 +>a : Symbol(a, Decl(assignmentCompatWithDiscriminatedUnion.ts, 22, 14)) +>b : Symbol(b, Decl(assignmentCompatWithDiscriminatedUnion.ts, 22, 20)) + + declare let s: S; +>s : Symbol(s, Decl(assignmentCompatWithDiscriminatedUnion.ts, 23, 15)) +>S : Symbol(S, Decl(assignmentCompatWithDiscriminatedUnion.ts, 18, 20)) + + declare let t: T; +>t : Symbol(t, Decl(assignmentCompatWithDiscriminatedUnion.ts, 24, 15)) +>T : Symbol(T, Decl(assignmentCompatWithDiscriminatedUnion.ts, 19, 32)) + + // S is assignable to T0 when S["a"] is 0 + // S is assignable to T2 when S["a"] is 2 + t = s; +>t : Symbol(t, Decl(assignmentCompatWithDiscriminatedUnion.ts, 24, 15)) +>s : Symbol(s, Decl(assignmentCompatWithDiscriminatedUnion.ts, 23, 15)) +} + +// Unmatched discriminants +namespace Example3 { +>Example3 : Symbol(Example3, Decl(assignmentCompatWithDiscriminatedUnion.ts, 29, 1)) + + type S = { a: 0 | 2, b: 4 }; +>S : Symbol(S, Decl(assignmentCompatWithDiscriminatedUnion.ts, 32, 20)) +>a : Symbol(a, Decl(assignmentCompatWithDiscriminatedUnion.ts, 33, 14)) +>b : Symbol(b, Decl(assignmentCompatWithDiscriminatedUnion.ts, 33, 24)) + + type T = { a: 0, b: 1 | 4 } // T0 +>T : Symbol(T, Decl(assignmentCompatWithDiscriminatedUnion.ts, 33, 32)) +>a : Symbol(a, Decl(assignmentCompatWithDiscriminatedUnion.ts, 34, 14)) +>b : Symbol(b, Decl(assignmentCompatWithDiscriminatedUnion.ts, 34, 20)) + + | { a: 1, b: 2 | 4 } // T1 +>a : Symbol(a, Decl(assignmentCompatWithDiscriminatedUnion.ts, 35, 14)) +>b : Symbol(b, Decl(assignmentCompatWithDiscriminatedUnion.ts, 35, 20)) + + | { a: 2, b: 3 }; // T2 +>a : Symbol(a, Decl(assignmentCompatWithDiscriminatedUnion.ts, 36, 14)) +>b : Symbol(b, Decl(assignmentCompatWithDiscriminatedUnion.ts, 36, 20)) + + declare let s: S; +>s : Symbol(s, Decl(assignmentCompatWithDiscriminatedUnion.ts, 37, 15)) +>S : Symbol(S, Decl(assignmentCompatWithDiscriminatedUnion.ts, 32, 20)) + + declare let t: T; +>t : Symbol(t, Decl(assignmentCompatWithDiscriminatedUnion.ts, 38, 15)) +>T : Symbol(T, Decl(assignmentCompatWithDiscriminatedUnion.ts, 33, 32)) + + // S is assignable to T0 when S["a"] is 0 + // S is *not* assignable to T1 when S["b"] is 4 + // S is *not* assignable to T2 when S["a"] is 2 + t = s; +>t : Symbol(t, Decl(assignmentCompatWithDiscriminatedUnion.ts, 38, 15)) +>s : Symbol(s, Decl(assignmentCompatWithDiscriminatedUnion.ts, 37, 15)) +} + +// Unmatched non-discriminants +namespace Example4 { +>Example4 : Symbol(Example4, Decl(assignmentCompatWithDiscriminatedUnion.ts, 44, 1)) + + type S = { a: 0 | 2, b: 4 }; +>S : Symbol(S, Decl(assignmentCompatWithDiscriminatedUnion.ts, 47, 20)) +>a : Symbol(a, Decl(assignmentCompatWithDiscriminatedUnion.ts, 48, 14)) +>b : Symbol(b, Decl(assignmentCompatWithDiscriminatedUnion.ts, 48, 24)) + + type T = { a: 0, b: 1 | 4 } // T0 +>T : Symbol(T, Decl(assignmentCompatWithDiscriminatedUnion.ts, 48, 32)) +>a : Symbol(a, Decl(assignmentCompatWithDiscriminatedUnion.ts, 49, 14)) +>b : Symbol(b, Decl(assignmentCompatWithDiscriminatedUnion.ts, 49, 20)) + + | { a: 1, b: 2 } // T1 +>a : Symbol(a, Decl(assignmentCompatWithDiscriminatedUnion.ts, 50, 14)) +>b : Symbol(b, Decl(assignmentCompatWithDiscriminatedUnion.ts, 50, 20)) + + | { a: 2, b: 3 | 4, c: string }; // T2 +>a : Symbol(a, Decl(assignmentCompatWithDiscriminatedUnion.ts, 51, 14)) +>b : Symbol(b, Decl(assignmentCompatWithDiscriminatedUnion.ts, 51, 20)) +>c : Symbol(c, Decl(assignmentCompatWithDiscriminatedUnion.ts, 51, 34)) + + declare let s: S; +>s : Symbol(s, Decl(assignmentCompatWithDiscriminatedUnion.ts, 52, 15)) +>S : Symbol(S, Decl(assignmentCompatWithDiscriminatedUnion.ts, 47, 20)) + + declare let t: T; +>t : Symbol(t, Decl(assignmentCompatWithDiscriminatedUnion.ts, 53, 15)) +>T : Symbol(T, Decl(assignmentCompatWithDiscriminatedUnion.ts, 48, 32)) + + // S is assignable to T0 when S["a"] is 0 + // S is *not* assignable to T2 when S["a"] is 2 as S is missing "c" + t = s; +>t : Symbol(t, Decl(assignmentCompatWithDiscriminatedUnion.ts, 53, 15)) +>s : Symbol(s, Decl(assignmentCompatWithDiscriminatedUnion.ts, 52, 15)) +} + +// Maximum discriminant combinations +namespace Example5 { +>Example5 : Symbol(Example5, Decl(assignmentCompatWithDiscriminatedUnion.ts, 58, 1)) + + // NOTE: The maximum number of discriminant type combinations is currently 25. + // 3 discriminant properties with 3 types a piece + // is 27 possible combinations. + type N = 0 | 1 | 2; +>N : Symbol(N, Decl(assignmentCompatWithDiscriminatedUnion.ts, 61, 20)) + + type S = { a: N, b: N, c: N }; +>S : Symbol(S, Decl(assignmentCompatWithDiscriminatedUnion.ts, 65, 23)) +>a : Symbol(a, Decl(assignmentCompatWithDiscriminatedUnion.ts, 66, 14)) +>N : Symbol(N, Decl(assignmentCompatWithDiscriminatedUnion.ts, 61, 20)) +>b : Symbol(b, Decl(assignmentCompatWithDiscriminatedUnion.ts, 66, 20)) +>N : Symbol(N, Decl(assignmentCompatWithDiscriminatedUnion.ts, 61, 20)) +>c : Symbol(c, Decl(assignmentCompatWithDiscriminatedUnion.ts, 66, 26)) +>N : Symbol(N, Decl(assignmentCompatWithDiscriminatedUnion.ts, 61, 20)) + + type T = { a: 0, b: N, c: N } +>T : Symbol(T, Decl(assignmentCompatWithDiscriminatedUnion.ts, 66, 34)) +>a : Symbol(a, Decl(assignmentCompatWithDiscriminatedUnion.ts, 67, 14)) +>b : Symbol(b, Decl(assignmentCompatWithDiscriminatedUnion.ts, 67, 20)) +>N : Symbol(N, Decl(assignmentCompatWithDiscriminatedUnion.ts, 61, 20)) +>c : Symbol(c, Decl(assignmentCompatWithDiscriminatedUnion.ts, 67, 26)) +>N : Symbol(N, Decl(assignmentCompatWithDiscriminatedUnion.ts, 61, 20)) + + | { a: 1, b: N, c: N } +>a : Symbol(a, Decl(assignmentCompatWithDiscriminatedUnion.ts, 68, 14)) +>b : Symbol(b, Decl(assignmentCompatWithDiscriminatedUnion.ts, 68, 20)) +>N : Symbol(N, Decl(assignmentCompatWithDiscriminatedUnion.ts, 61, 20)) +>c : Symbol(c, Decl(assignmentCompatWithDiscriminatedUnion.ts, 68, 26)) +>N : Symbol(N, Decl(assignmentCompatWithDiscriminatedUnion.ts, 61, 20)) + + | { a: 2, b: N, c: N } +>a : Symbol(a, Decl(assignmentCompatWithDiscriminatedUnion.ts, 69, 14)) +>b : Symbol(b, Decl(assignmentCompatWithDiscriminatedUnion.ts, 69, 20)) +>N : Symbol(N, Decl(assignmentCompatWithDiscriminatedUnion.ts, 61, 20)) +>c : Symbol(c, Decl(assignmentCompatWithDiscriminatedUnion.ts, 69, 26)) +>N : Symbol(N, Decl(assignmentCompatWithDiscriminatedUnion.ts, 61, 20)) + + | { a: N, b: 0, c: N } +>a : Symbol(a, Decl(assignmentCompatWithDiscriminatedUnion.ts, 70, 14)) +>N : Symbol(N, Decl(assignmentCompatWithDiscriminatedUnion.ts, 61, 20)) +>b : Symbol(b, Decl(assignmentCompatWithDiscriminatedUnion.ts, 70, 20)) +>c : Symbol(c, Decl(assignmentCompatWithDiscriminatedUnion.ts, 70, 26)) +>N : Symbol(N, Decl(assignmentCompatWithDiscriminatedUnion.ts, 61, 20)) + + | { a: N, b: 1, c: N } +>a : Symbol(a, Decl(assignmentCompatWithDiscriminatedUnion.ts, 71, 14)) +>N : Symbol(N, Decl(assignmentCompatWithDiscriminatedUnion.ts, 61, 20)) +>b : Symbol(b, Decl(assignmentCompatWithDiscriminatedUnion.ts, 71, 20)) +>c : Symbol(c, Decl(assignmentCompatWithDiscriminatedUnion.ts, 71, 26)) +>N : Symbol(N, Decl(assignmentCompatWithDiscriminatedUnion.ts, 61, 20)) + + | { a: N, b: 2, c: N } +>a : Symbol(a, Decl(assignmentCompatWithDiscriminatedUnion.ts, 72, 14)) +>N : Symbol(N, Decl(assignmentCompatWithDiscriminatedUnion.ts, 61, 20)) +>b : Symbol(b, Decl(assignmentCompatWithDiscriminatedUnion.ts, 72, 20)) +>c : Symbol(c, Decl(assignmentCompatWithDiscriminatedUnion.ts, 72, 26)) +>N : Symbol(N, Decl(assignmentCompatWithDiscriminatedUnion.ts, 61, 20)) + + | { a: N, b: N, c: 0 } +>a : Symbol(a, Decl(assignmentCompatWithDiscriminatedUnion.ts, 73, 14)) +>N : Symbol(N, Decl(assignmentCompatWithDiscriminatedUnion.ts, 61, 20)) +>b : Symbol(b, Decl(assignmentCompatWithDiscriminatedUnion.ts, 73, 20)) +>N : Symbol(N, Decl(assignmentCompatWithDiscriminatedUnion.ts, 61, 20)) +>c : Symbol(c, Decl(assignmentCompatWithDiscriminatedUnion.ts, 73, 26)) + + | { a: N, b: N, c: 1 } +>a : Symbol(a, Decl(assignmentCompatWithDiscriminatedUnion.ts, 74, 14)) +>N : Symbol(N, Decl(assignmentCompatWithDiscriminatedUnion.ts, 61, 20)) +>b : Symbol(b, Decl(assignmentCompatWithDiscriminatedUnion.ts, 74, 20)) +>N : Symbol(N, Decl(assignmentCompatWithDiscriminatedUnion.ts, 61, 20)) +>c : Symbol(c, Decl(assignmentCompatWithDiscriminatedUnion.ts, 74, 26)) + + | { a: N, b: N, c: 2 }; +>a : Symbol(a, Decl(assignmentCompatWithDiscriminatedUnion.ts, 75, 14)) +>N : Symbol(N, Decl(assignmentCompatWithDiscriminatedUnion.ts, 61, 20)) +>b : Symbol(b, Decl(assignmentCompatWithDiscriminatedUnion.ts, 75, 20)) +>N : Symbol(N, Decl(assignmentCompatWithDiscriminatedUnion.ts, 61, 20)) +>c : Symbol(c, Decl(assignmentCompatWithDiscriminatedUnion.ts, 75, 26)) + + declare let s: S; +>s : Symbol(s, Decl(assignmentCompatWithDiscriminatedUnion.ts, 76, 15)) +>S : Symbol(S, Decl(assignmentCompatWithDiscriminatedUnion.ts, 65, 23)) + + declare let t: T; +>t : Symbol(t, Decl(assignmentCompatWithDiscriminatedUnion.ts, 77, 15)) +>T : Symbol(T, Decl(assignmentCompatWithDiscriminatedUnion.ts, 66, 34)) + + // S *should* be assignable but the number of + // combinations is too complex. + t = s; +>t : Symbol(t, Decl(assignmentCompatWithDiscriminatedUnion.ts, 77, 15)) +>s : Symbol(s, Decl(assignmentCompatWithDiscriminatedUnion.ts, 76, 15)) +} + +// https://github.com/Microsoft/TypeScript/issues/14865 +namespace GH14865 { +>GH14865 : Symbol(GH14865, Decl(assignmentCompatWithDiscriminatedUnion.ts, 82, 1)) + + type Style1 = { +>Style1 : Symbol(Style1, Decl(assignmentCompatWithDiscriminatedUnion.ts, 85, 19)) + + type: "A"; +>type : Symbol(type, Decl(assignmentCompatWithDiscriminatedUnion.ts, 86, 19)) + + data: string; +>data : Symbol(data, Decl(assignmentCompatWithDiscriminatedUnion.ts, 87, 18)) + + } | { + type: "B"; +>type : Symbol(type, Decl(assignmentCompatWithDiscriminatedUnion.ts, 89, 9)) + + data: string; +>data : Symbol(data, Decl(assignmentCompatWithDiscriminatedUnion.ts, 90, 18)) + + }; + + type Style2 = { +>Style2 : Symbol(Style2, Decl(assignmentCompatWithDiscriminatedUnion.ts, 92, 6)) + + type: "A" | "B"; +>type : Symbol(type, Decl(assignmentCompatWithDiscriminatedUnion.ts, 94, 19)) + + data: string; +>data : Symbol(data, Decl(assignmentCompatWithDiscriminatedUnion.ts, 95, 24)) + } + + const a: Style2 = { type: "A", data: "whatevs" }; +>a : Symbol(a, Decl(assignmentCompatWithDiscriminatedUnion.ts, 99, 9)) +>Style2 : Symbol(Style2, Decl(assignmentCompatWithDiscriminatedUnion.ts, 92, 6)) +>type : Symbol(type, Decl(assignmentCompatWithDiscriminatedUnion.ts, 99, 23)) +>data : Symbol(data, Decl(assignmentCompatWithDiscriminatedUnion.ts, 99, 34)) + + let b: Style1; +>b : Symbol(b, Decl(assignmentCompatWithDiscriminatedUnion.ts, 100, 7)) +>Style1 : Symbol(Style1, Decl(assignmentCompatWithDiscriminatedUnion.ts, 85, 19)) + + a.type; // "A" | "B" +>a.type : Symbol(type, Decl(assignmentCompatWithDiscriminatedUnion.ts, 94, 19)) +>a : Symbol(a, Decl(assignmentCompatWithDiscriminatedUnion.ts, 99, 9)) +>type : Symbol(type, Decl(assignmentCompatWithDiscriminatedUnion.ts, 94, 19)) + + b.type; // "A" | "B" +>b.type : Symbol(type, Decl(assignmentCompatWithDiscriminatedUnion.ts, 86, 19), Decl(assignmentCompatWithDiscriminatedUnion.ts, 89, 9)) +>b : Symbol(b, Decl(assignmentCompatWithDiscriminatedUnion.ts, 100, 7)) +>type : Symbol(type, Decl(assignmentCompatWithDiscriminatedUnion.ts, 86, 19), Decl(assignmentCompatWithDiscriminatedUnion.ts, 89, 9)) + + b = a; // should be assignable +>b : Symbol(b, Decl(assignmentCompatWithDiscriminatedUnion.ts, 100, 7)) +>a : Symbol(a, Decl(assignmentCompatWithDiscriminatedUnion.ts, 99, 9)) +} + +// https://github.com/Microsoft/TypeScript/issues/30170 +namespace GH30170 { +>GH30170 : Symbol(GH30170, Decl(assignmentCompatWithDiscriminatedUnion.ts, 104, 1)) + + interface Blue { +>Blue : Symbol(Blue, Decl(assignmentCompatWithDiscriminatedUnion.ts, 107, 19)) + + color: 'blue' +>color : Symbol(Blue.color, Decl(assignmentCompatWithDiscriminatedUnion.ts, 108, 20)) + } + interface Yellow { +>Yellow : Symbol(Yellow, Decl(assignmentCompatWithDiscriminatedUnion.ts, 110, 5)) + + color?: 'yellow', +>color : Symbol(Yellow.color, Decl(assignmentCompatWithDiscriminatedUnion.ts, 111, 22)) + } + function draw(val: Blue | Yellow) { } +>draw : Symbol(draw, Decl(assignmentCompatWithDiscriminatedUnion.ts, 113, 5)) +>val : Symbol(val, Decl(assignmentCompatWithDiscriminatedUnion.ts, 114, 18)) +>Blue : Symbol(Blue, Decl(assignmentCompatWithDiscriminatedUnion.ts, 107, 19)) +>Yellow : Symbol(Yellow, Decl(assignmentCompatWithDiscriminatedUnion.ts, 110, 5)) + + function drawWithColor(currentColor: 'blue' | 'yellow' | undefined) { +>drawWithColor : Symbol(drawWithColor, Decl(assignmentCompatWithDiscriminatedUnion.ts, 114, 41)) +>currentColor : Symbol(currentColor, Decl(assignmentCompatWithDiscriminatedUnion.ts, 116, 27)) + + return draw({ color: currentColor }); +>draw : Symbol(draw, Decl(assignmentCompatWithDiscriminatedUnion.ts, 113, 5)) +>color : Symbol(color, Decl(assignmentCompatWithDiscriminatedUnion.ts, 117, 21)) +>currentColor : Symbol(currentColor, Decl(assignmentCompatWithDiscriminatedUnion.ts, 116, 27)) + } +} + +// https://github.com/Microsoft/TypeScript/issues/12052 +namespace GH12052 { +>GH12052 : Symbol(GH12052, Decl(assignmentCompatWithDiscriminatedUnion.ts, 119, 1)) + + interface ILinearAxis { type: "linear"; } +>ILinearAxis : Symbol(ILinearAxis, Decl(assignmentCompatWithDiscriminatedUnion.ts, 122, 19)) +>type : Symbol(ILinearAxis.type, Decl(assignmentCompatWithDiscriminatedUnion.ts, 123, 27)) + + interface ICategoricalAxis { type: "categorical"; } +>ICategoricalAxis : Symbol(ICategoricalAxis, Decl(assignmentCompatWithDiscriminatedUnion.ts, 123, 45)) +>type : Symbol(ICategoricalAxis.type, Decl(assignmentCompatWithDiscriminatedUnion.ts, 125, 32)) + + type IAxis = ILinearAxis | ICategoricalAxis; +>IAxis : Symbol(IAxis, Decl(assignmentCompatWithDiscriminatedUnion.ts, 125, 55)) +>ILinearAxis : Symbol(ILinearAxis, Decl(assignmentCompatWithDiscriminatedUnion.ts, 122, 19)) +>ICategoricalAxis : Symbol(ICategoricalAxis, Decl(assignmentCompatWithDiscriminatedUnion.ts, 123, 45)) + + type IAxisType = "linear" | "categorical"; +>IAxisType : Symbol(IAxisType, Decl(assignmentCompatWithDiscriminatedUnion.ts, 127, 48)) + + function getAxisType(): IAxisType { +>getAxisType : Symbol(getAxisType, Decl(assignmentCompatWithDiscriminatedUnion.ts, 128, 46)) +>IAxisType : Symbol(IAxisType, Decl(assignmentCompatWithDiscriminatedUnion.ts, 127, 48)) + + if (1 == 1) { + return "categorical"; + } else { + return "linear"; + } + } + + const bad: IAxis = { type: getAxisType() }; +>bad : Symbol(bad, Decl(assignmentCompatWithDiscriminatedUnion.ts, 138, 9)) +>IAxis : Symbol(IAxis, Decl(assignmentCompatWithDiscriminatedUnion.ts, 125, 55)) +>type : Symbol(type, Decl(assignmentCompatWithDiscriminatedUnion.ts, 138, 24)) +>getAxisType : Symbol(getAxisType, Decl(assignmentCompatWithDiscriminatedUnion.ts, 128, 46)) + + const good: IAxis = { type: undefined }; +>good : Symbol(good, Decl(assignmentCompatWithDiscriminatedUnion.ts, 139, 9)) +>IAxis : Symbol(IAxis, Decl(assignmentCompatWithDiscriminatedUnion.ts, 125, 55)) +>type : Symbol(type, Decl(assignmentCompatWithDiscriminatedUnion.ts, 139, 25)) +>undefined : Symbol(undefined) + + good.type = getAxisType(); +>good.type : Symbol(type, Decl(assignmentCompatWithDiscriminatedUnion.ts, 123, 27), Decl(assignmentCompatWithDiscriminatedUnion.ts, 125, 32)) +>good : Symbol(good, Decl(assignmentCompatWithDiscriminatedUnion.ts, 139, 9)) +>type : Symbol(type, Decl(assignmentCompatWithDiscriminatedUnion.ts, 123, 27), Decl(assignmentCompatWithDiscriminatedUnion.ts, 125, 32)) +>getAxisType : Symbol(getAxisType, Decl(assignmentCompatWithDiscriminatedUnion.ts, 128, 46)) +} + +// https://github.com/Microsoft/TypeScript/issues/18421 +namespace GH18421 { +>GH18421 : Symbol(GH18421, Decl(assignmentCompatWithDiscriminatedUnion.ts, 141, 1)) + + interface ThingTypeOne { +>ThingTypeOne : Symbol(ThingTypeOne, Decl(assignmentCompatWithDiscriminatedUnion.ts, 144, 19)) + + type: 'one'; +>type : Symbol(ThingTypeOne.type, Decl(assignmentCompatWithDiscriminatedUnion.ts, 145, 28)) + } + + interface ThingTypeTwo { +>ThingTypeTwo : Symbol(ThingTypeTwo, Decl(assignmentCompatWithDiscriminatedUnion.ts, 147, 5)) + + type: 'two'; +>type : Symbol(ThingTypeTwo.type, Decl(assignmentCompatWithDiscriminatedUnion.ts, 149, 28)) + } + + type ThingType = 'one' | 'two'; +>ThingType : Symbol(ThingType, Decl(assignmentCompatWithDiscriminatedUnion.ts, 151, 5)) + + type Thing = ThingTypeOne | ThingTypeTwo; +>Thing : Symbol(Thing, Decl(assignmentCompatWithDiscriminatedUnion.ts, 153, 35)) +>ThingTypeOne : Symbol(ThingTypeOne, Decl(assignmentCompatWithDiscriminatedUnion.ts, 144, 19)) +>ThingTypeTwo : Symbol(ThingTypeTwo, Decl(assignmentCompatWithDiscriminatedUnion.ts, 147, 5)) + + function makeNewThing(thingType: ThingType): Thing { +>makeNewThing : Symbol(makeNewThing, Decl(assignmentCompatWithDiscriminatedUnion.ts, 155, 45)) +>thingType : Symbol(thingType, Decl(assignmentCompatWithDiscriminatedUnion.ts, 157, 26)) +>ThingType : Symbol(ThingType, Decl(assignmentCompatWithDiscriminatedUnion.ts, 151, 5)) +>Thing : Symbol(Thing, Decl(assignmentCompatWithDiscriminatedUnion.ts, 153, 35)) + + return { + type: thingType +>type : Symbol(type, Decl(assignmentCompatWithDiscriminatedUnion.ts, 158, 16)) +>thingType : Symbol(thingType, Decl(assignmentCompatWithDiscriminatedUnion.ts, 157, 26)) + + }; + } +} + +// https://github.com/Microsoft/TypeScript/issues/15907 +namespace GH15907 { +>GH15907 : Symbol(GH15907, Decl(assignmentCompatWithDiscriminatedUnion.ts, 162, 1)) + + type Action = { type: 'activate' } | { type: 'disactivate' }; +>Action : Symbol(Action, Decl(assignmentCompatWithDiscriminatedUnion.ts, 165, 19)) +>type : Symbol(type, Decl(assignmentCompatWithDiscriminatedUnion.ts, 166, 19)) +>type : Symbol(type, Decl(assignmentCompatWithDiscriminatedUnion.ts, 166, 42)) + + function dispatchAction(action: Action): void { +>dispatchAction : Symbol(dispatchAction, Decl(assignmentCompatWithDiscriminatedUnion.ts, 166, 65)) +>action : Symbol(action, Decl(assignmentCompatWithDiscriminatedUnion.ts, 168, 28)) +>Action : Symbol(Action, Decl(assignmentCompatWithDiscriminatedUnion.ts, 165, 19)) + + } + + const active = true; +>active : Symbol(active, Decl(assignmentCompatWithDiscriminatedUnion.ts, 172, 9)) + + dispatchAction({ type : (active? 'disactivate' : 'activate') }); +>dispatchAction : Symbol(dispatchAction, Decl(assignmentCompatWithDiscriminatedUnion.ts, 166, 65)) +>type : Symbol(type, Decl(assignmentCompatWithDiscriminatedUnion.ts, 174, 20)) +>active : Symbol(active, Decl(assignmentCompatWithDiscriminatedUnion.ts, 172, 9)) +} + +// https://github.com/Microsoft/TypeScript/issues/20889 +namespace GH20889 { +>GH20889 : Symbol(GH20889, Decl(assignmentCompatWithDiscriminatedUnion.ts, 175, 1)) + + interface A1 { +>A1 : Symbol(A1, Decl(assignmentCompatWithDiscriminatedUnion.ts, 178, 19)) + + type: "A1"; +>type : Symbol(A1.type, Decl(assignmentCompatWithDiscriminatedUnion.ts, 179, 18)) + } + interface A2 { +>A2 : Symbol(A2, Decl(assignmentCompatWithDiscriminatedUnion.ts, 181, 5)) + + type: "A2"; +>type : Symbol(A2.type, Decl(assignmentCompatWithDiscriminatedUnion.ts, 182, 18)) + } + type AU = A1 | A2; +>AU : Symbol(AU, Decl(assignmentCompatWithDiscriminatedUnion.ts, 184, 5)) +>A1 : Symbol(A1, Decl(assignmentCompatWithDiscriminatedUnion.ts, 178, 19)) +>A2 : Symbol(A2, Decl(assignmentCompatWithDiscriminatedUnion.ts, 181, 5)) + + function foo(obj1: AU) { +>foo : Symbol(foo, Decl(assignmentCompatWithDiscriminatedUnion.ts, 185, 22)) +>obj1 : Symbol(obj1, Decl(assignmentCompatWithDiscriminatedUnion.ts, 187, 17)) +>AU : Symbol(AU, Decl(assignmentCompatWithDiscriminatedUnion.ts, 184, 5)) + + const obj2: AU = { +>obj2 : Symbol(obj2, Decl(assignmentCompatWithDiscriminatedUnion.ts, 188, 13)) +>AU : Symbol(AU, Decl(assignmentCompatWithDiscriminatedUnion.ts, 184, 5)) + + type: obj1.type +>type : Symbol(type, Decl(assignmentCompatWithDiscriminatedUnion.ts, 188, 26)) +>obj1.type : Symbol(type, Decl(assignmentCompatWithDiscriminatedUnion.ts, 179, 18), Decl(assignmentCompatWithDiscriminatedUnion.ts, 182, 18)) +>obj1 : Symbol(obj1, Decl(assignmentCompatWithDiscriminatedUnion.ts, 187, 17)) +>type : Symbol(type, Decl(assignmentCompatWithDiscriminatedUnion.ts, 179, 18), Decl(assignmentCompatWithDiscriminatedUnion.ts, 182, 18)) + + }; + } +} diff --git a/tests/baselines/reference/assignmentCompatWithDiscriminatedUnion.types b/tests/baselines/reference/assignmentCompatWithDiscriminatedUnion.types new file mode 100644 index 0000000000000..76bdb2716d20e --- /dev/null +++ b/tests/baselines/reference/assignmentCompatWithDiscriminatedUnion.types @@ -0,0 +1,466 @@ +=== tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithDiscriminatedUnion.ts === +// see 'typeRelatedToDiscriminatedType' in checker.ts: + +// IteratorResult +namespace Example1 { +>Example1 : typeof Example1 + + type S = { done: boolean, value: number }; +>S : S +>done : boolean +>value : number + + type T = +>T : T + + | { done: true, value: number } // T0 +>done : true +>true : true +>value : number + + | { done: false, value: number }; // T1 +>done : false +>false : false +>value : number + + declare let s: S; +>s : S + + declare let t: T; +>t : T + + // S is assignable to T0 when S["done"] is true + // S is assignable to T1 when S["done"] is false + t = s; +>t = s : S +>t : T +>s : S +} + +// Dropping constituents of T +namespace Example2 { +>Example2 : typeof Example2 + + type S = { a: 0 | 2, b: 4 }; +>S : S +>a : 0 | 2 +>b : 4 + + type T = { a: 0, b: 1 | 4 } // T0 +>T : T +>a : 0 +>b : 4 | 1 + + | { a: 1, b: 2 } // T1 +>a : 1 +>b : 2 + + | { a: 2, b: 3 | 4 }; // T2 +>a : 2 +>b : 4 | 3 + + declare let s: S; +>s : S + + declare let t: T; +>t : T + + // S is assignable to T0 when S["a"] is 0 + // S is assignable to T2 when S["a"] is 2 + t = s; +>t = s : S +>t : T +>s : S +} + +// Unmatched discriminants +namespace Example3 { +>Example3 : typeof Example3 + + type S = { a: 0 | 2, b: 4 }; +>S : S +>a : 0 | 2 +>b : 4 + + type T = { a: 0, b: 1 | 4 } // T0 +>T : T +>a : 0 +>b : 4 | 1 + + | { a: 1, b: 2 | 4 } // T1 +>a : 1 +>b : 2 | 4 + + | { a: 2, b: 3 }; // T2 +>a : 2 +>b : 3 + + declare let s: S; +>s : S + + declare let t: T; +>t : T + + // S is assignable to T0 when S["a"] is 0 + // S is *not* assignable to T1 when S["b"] is 4 + // S is *not* assignable to T2 when S["a"] is 2 + t = s; +>t = s : S +>t : T +>s : S +} + +// Unmatched non-discriminants +namespace Example4 { +>Example4 : typeof Example4 + + type S = { a: 0 | 2, b: 4 }; +>S : S +>a : 0 | 2 +>b : 4 + + type T = { a: 0, b: 1 | 4 } // T0 +>T : T +>a : 0 +>b : 4 | 1 + + | { a: 1, b: 2 } // T1 +>a : 1 +>b : 2 + + | { a: 2, b: 3 | 4, c: string }; // T2 +>a : 2 +>b : 4 | 3 +>c : string + + declare let s: S; +>s : S + + declare let t: T; +>t : T + + // S is assignable to T0 when S["a"] is 0 + // S is *not* assignable to T2 when S["a"] is 2 as S is missing "c" + t = s; +>t = s : S +>t : T +>s : S +} + +// Maximum discriminant combinations +namespace Example5 { +>Example5 : typeof Example5 + + // NOTE: The maximum number of discriminant type combinations is currently 25. + // 3 discriminant properties with 3 types a piece + // is 27 possible combinations. + type N = 0 | 1 | 2; +>N : 0 | 2 | 1 + + type S = { a: N, b: N, c: N }; +>S : S +>a : 0 | 2 | 1 +>b : 0 | 2 | 1 +>c : 0 | 2 | 1 + + type T = { a: 0, b: N, c: N } +>T : T +>a : 0 +>b : 0 | 2 | 1 +>c : 0 | 2 | 1 + + | { a: 1, b: N, c: N } +>a : 1 +>b : 0 | 2 | 1 +>c : 0 | 2 | 1 + + | { a: 2, b: N, c: N } +>a : 2 +>b : 0 | 2 | 1 +>c : 0 | 2 | 1 + + | { a: N, b: 0, c: N } +>a : 0 | 2 | 1 +>b : 0 +>c : 0 | 2 | 1 + + | { a: N, b: 1, c: N } +>a : 0 | 2 | 1 +>b : 1 +>c : 0 | 2 | 1 + + | { a: N, b: 2, c: N } +>a : 0 | 2 | 1 +>b : 2 +>c : 0 | 2 | 1 + + | { a: N, b: N, c: 0 } +>a : 0 | 2 | 1 +>b : 0 | 2 | 1 +>c : 0 + + | { a: N, b: N, c: 1 } +>a : 0 | 2 | 1 +>b : 0 | 2 | 1 +>c : 1 + + | { a: N, b: N, c: 2 }; +>a : 0 | 2 | 1 +>b : 0 | 2 | 1 +>c : 2 + + declare let s: S; +>s : S + + declare let t: T; +>t : T + + // S *should* be assignable but the number of + // combinations is too complex. + t = s; +>t = s : S +>t : T +>s : S +} + +// https://github.com/Microsoft/TypeScript/issues/14865 +namespace GH14865 { +>GH14865 : typeof GH14865 + + type Style1 = { +>Style1 : Style1 + + type: "A"; +>type : "A" + + data: string; +>data : string + + } | { + type: "B"; +>type : "B" + + data: string; +>data : string + + }; + + type Style2 = { +>Style2 : Style2 + + type: "A" | "B"; +>type : "A" | "B" + + data: string; +>data : string + } + + const a: Style2 = { type: "A", data: "whatevs" }; +>a : Style2 +>{ type: "A", data: "whatevs" } : { type: "A"; data: string; } +>type : "A" +>"A" : "A" +>data : string +>"whatevs" : "whatevs" + + let b: Style1; +>b : Style1 + + a.type; // "A" | "B" +>a.type : "A" | "B" +>a : Style2 +>type : "A" | "B" + + b.type; // "A" | "B" +>b.type : "A" | "B" +>b : Style1 +>type : "A" | "B" + + b = a; // should be assignable +>b = a : Style2 +>b : Style1 +>a : Style2 +} + +// https://github.com/Microsoft/TypeScript/issues/30170 +namespace GH30170 { +>GH30170 : typeof GH30170 + + interface Blue { + color: 'blue' +>color : "blue" + } + interface Yellow { + color?: 'yellow', +>color : "yellow" + } + function draw(val: Blue | Yellow) { } +>draw : (val: Blue | Yellow) => void +>val : Blue | Yellow + + function drawWithColor(currentColor: 'blue' | 'yellow' | undefined) { +>drawWithColor : (currentColor: "blue" | "yellow") => void +>currentColor : "blue" | "yellow" + + return draw({ color: currentColor }); +>draw({ color: currentColor }) : void +>draw : (val: Blue | Yellow) => void +>{ color: currentColor } : { color: "blue" | "yellow"; } +>color : "blue" | "yellow" +>currentColor : "blue" | "yellow" + } +} + +// https://github.com/Microsoft/TypeScript/issues/12052 +namespace GH12052 { +>GH12052 : typeof GH12052 + + interface ILinearAxis { type: "linear"; } +>type : "linear" + + interface ICategoricalAxis { type: "categorical"; } +>type : "categorical" + + type IAxis = ILinearAxis | ICategoricalAxis; +>IAxis : IAxis + + type IAxisType = "linear" | "categorical"; +>IAxisType : IAxisType + + function getAxisType(): IAxisType { +>getAxisType : () => IAxisType + + if (1 == 1) { +>1 == 1 : boolean +>1 : 1 +>1 : 1 + + return "categorical"; +>"categorical" : "categorical" + + } else { + return "linear"; +>"linear" : "linear" + } + } + + const bad: IAxis = { type: getAxisType() }; +>bad : IAxis +>{ type: getAxisType() } : { type: IAxisType; } +>type : IAxisType +>getAxisType() : IAxisType +>getAxisType : () => IAxisType + + const good: IAxis = { type: undefined }; +>good : IAxis +>{ type: undefined } : { type: undefined; } +>type : undefined +>undefined : undefined + + good.type = getAxisType(); +>good.type = getAxisType() : IAxisType +>good.type : IAxisType +>good : IAxis +>type : IAxisType +>getAxisType() : IAxisType +>getAxisType : () => IAxisType +} + +// https://github.com/Microsoft/TypeScript/issues/18421 +namespace GH18421 { +>GH18421 : typeof GH18421 + + interface ThingTypeOne { + type: 'one'; +>type : "one" + } + + interface ThingTypeTwo { + type: 'two'; +>type : "two" + } + + type ThingType = 'one' | 'two'; +>ThingType : ThingType + + type Thing = ThingTypeOne | ThingTypeTwo; +>Thing : Thing + + function makeNewThing(thingType: ThingType): Thing { +>makeNewThing : (thingType: ThingType) => Thing +>thingType : ThingType + + return { +>{ type: thingType } : { type: ThingType; } + + type: thingType +>type : ThingType +>thingType : ThingType + + }; + } +} + +// https://github.com/Microsoft/TypeScript/issues/15907 +namespace GH15907 { +>GH15907 : typeof GH15907 + + type Action = { type: 'activate' } | { type: 'disactivate' }; +>Action : Action +>type : "activate" +>type : "disactivate" + + function dispatchAction(action: Action): void { +>dispatchAction : (action: Action) => void +>action : Action + + } + + const active = true; +>active : true +>true : true + + dispatchAction({ type : (active? 'disactivate' : 'activate') }); +>dispatchAction({ type : (active? 'disactivate' : 'activate') }) : void +>dispatchAction : (action: Action) => void +>{ type : (active? 'disactivate' : 'activate') } : { type: "activate" | "disactivate"; } +>type : "activate" | "disactivate" +>(active? 'disactivate' : 'activate') : "activate" | "disactivate" +>active? 'disactivate' : 'activate' : "activate" | "disactivate" +>active : true +>'disactivate' : "disactivate" +>'activate' : "activate" +} + +// https://github.com/Microsoft/TypeScript/issues/20889 +namespace GH20889 { +>GH20889 : typeof GH20889 + + interface A1 { + type: "A1"; +>type : "A1" + } + interface A2 { + type: "A2"; +>type : "A2" + } + type AU = A1 | A2; +>AU : AU + + function foo(obj1: AU) { +>foo : (obj1: AU) => void +>obj1 : AU + + const obj2: AU = { +>obj2 : AU +>{ type: obj1.type } : { type: "A1" | "A2"; } + + type: obj1.type +>type : "A1" | "A2" +>obj1.type : "A1" | "A2" +>obj1 : AU +>type : "A1" | "A2" + + }; + } +} diff --git a/tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithDiscriminatedUnion.ts b/tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithDiscriminatedUnion.ts new file mode 100644 index 0000000000000..e264df259adee --- /dev/null +++ b/tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithDiscriminatedUnion.ts @@ -0,0 +1,193 @@ +// see 'typeRelatedToDiscriminatedType' in checker.ts: + +// IteratorResult +namespace Example1 { + type S = { done: boolean, value: number }; + type T = + | { done: true, value: number } // T0 + | { done: false, value: number }; // T1 + + declare let s: S; + declare let t: T; + + // S is assignable to T0 when S["done"] is true + // S is assignable to T1 when S["done"] is false + t = s; +} + +// Dropping constituents of T +namespace Example2 { + type S = { a: 0 | 2, b: 4 }; + type T = { a: 0, b: 1 | 4 } // T0 + | { a: 1, b: 2 } // T1 + | { a: 2, b: 3 | 4 }; // T2 + declare let s: S; + declare let t: T; + + // S is assignable to T0 when S["a"] is 0 + // S is assignable to T2 when S["a"] is 2 + t = s; +} + +// Unmatched discriminants +namespace Example3 { + type S = { a: 0 | 2, b: 4 }; + type T = { a: 0, b: 1 | 4 } // T0 + | { a: 1, b: 2 | 4 } // T1 + | { a: 2, b: 3 }; // T2 + declare let s: S; + declare let t: T; + + // S is assignable to T0 when S["a"] is 0 + // S is *not* assignable to T1 when S["b"] is 4 + // S is *not* assignable to T2 when S["a"] is 2 + t = s; +} + +// Unmatched non-discriminants +namespace Example4 { + type S = { a: 0 | 2, b: 4 }; + type T = { a: 0, b: 1 | 4 } // T0 + | { a: 1, b: 2 } // T1 + | { a: 2, b: 3 | 4, c: string }; // T2 + declare let s: S; + declare let t: T; + + // S is assignable to T0 when S["a"] is 0 + // S is *not* assignable to T2 when S["a"] is 2 as S is missing "c" + t = s; +} + +// Maximum discriminant combinations +namespace Example5 { + // NOTE: The maximum number of discriminant type combinations is currently 25. + // 3 discriminant properties with 3 types a piece + // is 27 possible combinations. + type N = 0 | 1 | 2; + type S = { a: N, b: N, c: N }; + type T = { a: 0, b: N, c: N } + | { a: 1, b: N, c: N } + | { a: 2, b: N, c: N } + | { a: N, b: 0, c: N } + | { a: N, b: 1, c: N } + | { a: N, b: 2, c: N } + | { a: N, b: N, c: 0 } + | { a: N, b: N, c: 1 } + | { a: N, b: N, c: 2 }; + declare let s: S; + declare let t: T; + + // S *should* be assignable but the number of + // combinations is too complex. + t = s; +} + +// https://github.com/Microsoft/TypeScript/issues/14865 +namespace GH14865 { + type Style1 = { + type: "A"; + data: string; + } | { + type: "B"; + data: string; + }; + + type Style2 = { + type: "A" | "B"; + data: string; + } + + const a: Style2 = { type: "A", data: "whatevs" }; + let b: Style1; + a.type; // "A" | "B" + b.type; // "A" | "B" + b = a; // should be assignable +} + +// https://github.com/Microsoft/TypeScript/issues/30170 +namespace GH30170 { + interface Blue { + color: 'blue' + } + interface Yellow { + color?: 'yellow', + } + function draw(val: Blue | Yellow) { } + + function drawWithColor(currentColor: 'blue' | 'yellow' | undefined) { + return draw({ color: currentColor }); + } +} + +// https://github.com/Microsoft/TypeScript/issues/12052 +namespace GH12052 { + interface ILinearAxis { type: "linear"; } + + interface ICategoricalAxis { type: "categorical"; } + + type IAxis = ILinearAxis | ICategoricalAxis; + type IAxisType = "linear" | "categorical"; + + function getAxisType(): IAxisType { + if (1 == 1) { + return "categorical"; + } else { + return "linear"; + } + } + + const bad: IAxis = { type: getAxisType() }; + const good: IAxis = { type: undefined }; + good.type = getAxisType(); +} + +// https://github.com/Microsoft/TypeScript/issues/18421 +namespace GH18421 { + interface ThingTypeOne { + type: 'one'; + } + + interface ThingTypeTwo { + type: 'two'; + } + + type ThingType = 'one' | 'two'; + + type Thing = ThingTypeOne | ThingTypeTwo; + + function makeNewThing(thingType: ThingType): Thing { + return { + type: thingType + }; + } +} + +// https://github.com/Microsoft/TypeScript/issues/15907 +namespace GH15907 { + type Action = { type: 'activate' } | { type: 'disactivate' }; + + function dispatchAction(action: Action): void { + + } + + const active = true; + + dispatchAction({ type : (active? 'disactivate' : 'activate') }); +} + +// https://github.com/Microsoft/TypeScript/issues/20889 +namespace GH20889 { + interface A1 { + type: "A1"; + } + interface A2 { + type: "A2"; + } + type AU = A1 | A2; + + function foo(obj1: AU) { + const obj2: AU = { + type: obj1.type + }; + } +} \ No newline at end of file