Merge pull request #30779 from Microsoft/relateDiscriminants

Relate source types covered by a target discriminated union
This commit is contained in:
Ron Buckton 2019-04-29 16:58:27 -07:00 committed by GitHub
commit 2d8527f3f0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 1897 additions and 70 deletions

View file

@ -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<Type[]>(sourcePropertiesFiltered.length);
const excludedProperties = createUnderscoreEscapedMap<true>();
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<true> | 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<true> | 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<true> | 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.

View file

@ -2284,4 +2284,29 @@ namespace ts {
}
return result;
}
export function cartesianProduct<T>(arrays: readonly T[][]) {
const result: T[][] = [];
cartesianProductWorker(arrays, result, /*outer*/ undefined, 0);
return result;
}
function cartesianProductWorker<T>(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);
}
}
}
}

View file

@ -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
};
}
}

View file

@ -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 = {}));

View file

@ -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))
};
}
}

View file

@ -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"
};
}
}

View file

@ -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
};
}
}