Preserve literal types in contextual unions (#19966)

* Cherrypick non-comparability related changes from prolific literals PR

* Renames and other style changes

* Accept changes to new tests

* Exclude the domain root from contextual typing literals except for type variables

* Readd simple preservation fix

* Add huge map test

* Revert changes to widening on destructuring initalizers

* Use tristate for subtype-reduction type

* Rename type and argument

* Move longer-running test to user suite
This commit is contained in:
Wesley Wigham 2017-12-11 18:03:38 -05:00 committed by GitHub
parent d01f4d140a
commit eba15b5990
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 11007 additions and 49 deletions

View file

@ -4338,7 +4338,7 @@ namespace ts {
type = getTypeWithFacts(type, TypeFacts.NEUndefined);
}
return declaration.initializer ?
getUnionType([type, checkExpressionCached(declaration.initializer)], /*subtypeReduction*/ true) :
getUnionType([type, checkExpressionCached(declaration.initializer)], UnionReduction.Subtype) :
type;
}
@ -4506,7 +4506,7 @@ namespace ts {
}
}
const type = jsDocType || getUnionType(types, /*subtypeReduction*/ true);
const type = jsDocType || getUnionType(types, UnionReduction.Subtype);
return getWidenedType(addOptionality(type, definedInMethod && !definedInConstructor));
}
@ -5339,7 +5339,7 @@ namespace ts {
}
}
if (memberTypeList.length) {
const enumType = getUnionType(memberTypeList, /*subtypeReduction*/ false, symbol, /*aliasTypeArguments*/ undefined);
const enumType = getUnionType(memberTypeList, UnionReduction.Literal, symbol, /*aliasTypeArguments*/ undefined);
if (enumType.flags & TypeFlags.Union) {
enumType.flags |= TypeFlags.EnumLiteral;
enumType.symbol = symbol;
@ -5902,7 +5902,7 @@ namespace ts {
if (unionSignatures.length > 1) {
let thisParameter = signature.thisParameter;
if (forEach(unionSignatures, sig => sig.thisParameter)) {
const thisType = getUnionType(map(unionSignatures, sig => getTypeOfSymbol(sig.thisParameter) || anyType), /*subtypeReduction*/ true);
const thisType = getUnionType(map(unionSignatures, sig => getTypeOfSymbol(sig.thisParameter) || anyType), UnionReduction.Subtype);
thisParameter = createSymbolWithType(signature.thisParameter, thisType);
}
s = cloneSignature(signature);
@ -5928,7 +5928,7 @@ namespace ts {
indexTypes.push(indexInfo.type);
isAnyReadonly = isAnyReadonly || indexInfo.isReadonly;
}
return createIndexInfo(getUnionType(indexTypes, /*subtypeReduction*/ true), isAnyReadonly);
return createIndexInfo(getUnionType(indexTypes, UnionReduction.Subtype), isAnyReadonly);
}
function resolveUnionTypeMembers(type: UnionType) {
@ -6616,7 +6616,7 @@ namespace ts {
}
}
if (propTypes.length) {
return getUnionType(propTypes, /*subtypeReduction*/ true);
return getUnionType(propTypes, UnionReduction.Subtype);
}
}
return undefined;
@ -7003,7 +7003,7 @@ namespace ts {
type = instantiateType(getReturnTypeOfSignature(signature.target), signature.mapper);
}
else if (signature.unionSignatures) {
type = getUnionType(map(signature.unionSignatures, getReturnTypeOfSignature), /*subtypeReduction*/ true);
type = getUnionType(map(signature.unionSignatures, getReturnTypeOfSignature), UnionReduction.Subtype);
}
else {
type = getReturnTypeFromBody(<FunctionLikeDeclaration>signature.declaration);
@ -7845,7 +7845,7 @@ namespace ts {
// expression constructs such as array literals and the || and ?: operators). Named types can
// circularly reference themselves and therefore cannot be subtype reduced during their declaration.
// For example, "type Item = string | (() => Item" is a named type that circularly references itself.
function getUnionType(types: Type[], subtypeReduction?: boolean, aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type {
function getUnionType(types: Type[], unionReduction: UnionReduction = UnionReduction.Literal, aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type {
if (types.length === 0) {
return neverType;
}
@ -7857,11 +7857,15 @@ namespace ts {
if (typeSet.containsAny) {
return anyType;
}
if (subtypeReduction) {
removeSubtypes(typeSet);
}
else if (typeSet.containsLiteralOrUniqueESSymbol) {
removeRedundantLiteralTypes(typeSet);
switch (unionReduction) {
case UnionReduction.Literal:
if (typeSet.containsLiteralOrUniqueESSymbol) {
removeRedundantLiteralTypes(typeSet);
}
break;
case UnionReduction.Subtype:
removeSubtypes(typeSet);
break;
}
if (typeSet.length === 0) {
return typeSet.containsNull ? typeSet.containsNonWideningType ? nullType : nullWideningType :
@ -7937,7 +7941,7 @@ namespace ts {
function getTypeFromUnionTypeNode(node: UnionTypeNode): Type {
const links = getNodeLinks(node);
if (!links.resolvedType) {
links.resolvedType = getUnionType(map(node.types, getTypeFromTypeNode), /*subtypeReduction*/ false,
links.resolvedType = getUnionType(map(node.types, getTypeFromTypeNode), UnionReduction.Literal,
getAliasSymbolForTypeNode(node), getAliasTypeArgumentsForTypeNode(node));
}
return links.resolvedType;
@ -8012,7 +8016,7 @@ namespace ts {
// the form X & A & Y | X & B & Y and recursively reduce until no union type constituents remain.
const unionType = <UnionType>typeSet[unionIndex];
return getUnionType(map(unionType.types, t => getIntersectionType(replaceElement(typeSet, unionIndex, t))),
/*subtypeReduction*/ false, aliasSymbol, aliasTypeArguments);
UnionReduction.Literal, aliasSymbol, aliasTypeArguments);
}
const id = getTypeListId(typeSet);
let type = intersectionTypes.get(id);
@ -8851,7 +8855,7 @@ namespace ts {
}
}
if (type.flags & TypeFlags.Union && !(type.flags & TypeFlags.Primitive)) {
return getUnionType(instantiateTypes((<UnionType>type).types, mapper), /*subtypeReduction*/ false, type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper));
return getUnionType(instantiateTypes((<UnionType>type).types, mapper), UnionReduction.Literal, type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper));
}
if (type.flags & TypeFlags.Intersection) {
return getIntersectionType(instantiateTypes((<IntersectionType>type).types, mapper), type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper));
@ -10713,7 +10717,7 @@ namespace ts {
const primaryTypes = filter(types, t => !(t.flags & TypeFlags.Nullable));
return primaryTypes.length ?
getNullableType(getSupertypeOrUnion(primaryTypes), getFalsyFlagsOfTypes(types) & TypeFlags.Nullable) :
getUnionType(types, /*subtypeReduction*/ true);
getUnionType(types, UnionReduction.Subtype);
}
// Return the leftmost type for which no type to the right is a subtype.
@ -11004,7 +11008,7 @@ namespace ts {
// Widening an empty object literal transitions from a highly restrictive type to
// a highly inclusive one. For that reason we perform subtype reduction here if the
// union includes empty object types (e.g. reducing {} | string to just {}).
return getUnionType(widenedTypes, some(widenedTypes, isEmptyObjectType));
return getUnionType(widenedTypes, some(widenedTypes, isEmptyObjectType) ? UnionReduction.Subtype : UnionReduction.Literal);
}
if (isArrayType(type) || isTupleType(type)) {
return createTypeReference((<TypeReference>type).target, sameMap((<TypeReference>type).typeArguments, getWidenedType));
@ -11251,7 +11255,7 @@ namespace ts {
function inferTargetType(sourceType: Type): Type {
inference.candidates = undefined;
inferTypes(inferences, sourceType, templateType, 0, mappedTypeStack);
return inference.candidates ? getUnionType(inference.candidates, /*subtypeReduction*/ true) : emptyObjectType;
return inference.candidates ? getUnionType(inference.candidates, UnionReduction.Subtype) : emptyObjectType;
}
}
@ -11610,7 +11614,7 @@ namespace ts {
if (candidates.length > 1) {
const objectLiterals = filter(candidates, isObjectLiteralType);
if (objectLiterals.length) {
const objectLiteralsType = getWidenedType(getUnionType(objectLiterals, /*subtypeReduction*/ true));
const objectLiteralsType = getWidenedType(getUnionType(objectLiterals, UnionReduction.Subtype));
return concatenate(filter(candidates, t => !isObjectLiteralType(t)), [objectLiteralsType]);
}
}
@ -11637,7 +11641,7 @@ namespace ts {
// union types were requested or if all inferences were made from the return type position, infer a
// union type. Otherwise, infer a common supertype.
const unwidenedType = inference.priority & InferencePriority.Contravariant ? getCommonSubtype(baseCandidates) :
context.flags & InferenceFlags.InferUnionTypes || inference.priority & InferencePriority.ReturnType ? getUnionType(baseCandidates, /*subtypeReduction*/ true) :
context.flags & InferenceFlags.InferUnionTypes || inference.priority & InferencePriority.ReturnType ? getUnionType(baseCandidates, UnionReduction.Subtype) :
getCommonSupertype(baseCandidates);
inferredType = getWidenedType(unwidenedType);
}
@ -12211,7 +12215,7 @@ namespace ts {
// 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.
function mapType(type: Type, mapper: (t: Type) => Type): Type {
function mapType(type: Type, mapper: (t: Type) => Type, noReductions?: boolean): Type {
if (!(type.flags & TypeFlags.Union)) {
return mapper(type);
}
@ -12232,7 +12236,7 @@ namespace ts {
}
}
}
return mappedTypes ? getUnionType(mappedTypes) : mappedType;
return mappedTypes ? getUnionType(mappedTypes, noReductions ? UnionReduction.None : UnionReduction.Literal) : mappedType;
}
function extractTypesOfKind(type: Type, kind: TypeFlags) {
@ -12291,7 +12295,7 @@ namespace ts {
return elementType.flags & TypeFlags.Never ?
autoArrayType :
createArrayType(elementType.flags & TypeFlags.Union ?
getUnionType((<UnionType>elementType).types, /*subtypeReduction*/ true) :
getUnionType((<UnionType>elementType).types, UnionReduction.Subtype) :
elementType);
}
@ -12324,7 +12328,7 @@ namespace ts {
// At flow control branch or loop junctions, if the type along every antecedent code path
// is an evolving array type, we construct a combined evolving array type. Otherwise we
// finalize all evolving array types.
function getUnionOrEvolvingArrayType(types: Type[], subtypeReduction: boolean) {
function getUnionOrEvolvingArrayType(types: Type[], subtypeReduction: UnionReduction) {
return isEvolvingArrayTypeList(types) ?
getEvolvingArrayType(getUnionType(map(types, getElementTypeOfEvolvingArrayType))) :
getUnionType(sameMap(types, finalizeEvolvingArrayType), subtypeReduction);
@ -12616,7 +12620,7 @@ namespace ts {
seenIncomplete = true;
}
}
return createFlowType(getUnionOrEvolvingArrayType(antecedentTypes, subtypeReduction), seenIncomplete);
return createFlowType(getUnionOrEvolvingArrayType(antecedentTypes, subtypeReduction ? UnionReduction.Subtype : UnionReduction.Literal), seenIncomplete);
}
function getTypeAtFlowLoopLabel(flow: FlowLabel): FlowType {
@ -12645,7 +12649,7 @@ namespace ts {
// path that leads to the top.
for (let i = flowLoopStart; i < flowLoopCount; i++) {
if (flowLoopNodes[i] === flow && flowLoopKeys[i] === key && flowLoopTypes[i].length) {
return createFlowType(getUnionOrEvolvingArrayType(flowLoopTypes[i], /*subtypeReduction*/ false), /*incomplete*/ true);
return createFlowType(getUnionOrEvolvingArrayType(flowLoopTypes[i], UnionReduction.Literal), /*incomplete*/ true);
}
}
// Add the flow loop junction and reference to the in-process stack and analyze
@ -12687,7 +12691,7 @@ namespace ts {
}
// The result is incomplete if the first antecedent (the non-looping control flow path)
// is incomplete.
const result = getUnionOrEvolvingArrayType(antecedentTypes, subtypeReduction);
const result = getUnionOrEvolvingArrayType(antecedentTypes, subtypeReduction ? UnionReduction.Subtype : UnionReduction.Literal);
if (isIncomplete(firstAntecedentType)) {
return createFlowType(result, /*incomplete*/ true);
}
@ -14052,11 +14056,11 @@ namespace ts {
return mapType(type, t => {
const prop = t.flags & TypeFlags.StructuredType ? getPropertyOfType(t, name) : undefined;
return prop ? getTypeOfSymbol(prop) : undefined;
});
}, /*noReductions*/ true);
}
function getIndexTypeOfContextualType(type: Type, kind: IndexKind) {
return mapType(type, t => getIndexTypeOfStructuredType(t, kind));
return mapType(type, t => getIndexTypeOfStructuredType(t, kind), /*noReductions*/ true);
}
// Return true if the given contextual type is a tuple-like type
@ -14463,7 +14467,7 @@ namespace ts {
}
}
return createArrayType(elementTypes.length ?
getUnionType(elementTypes, /*subtypeReduction*/ true) :
getUnionType(elementTypes, UnionReduction.Subtype) :
strictNullChecks ? implicitNeverType : undefinedWideningType);
}
@ -14542,7 +14546,7 @@ namespace ts {
propTypes.push(getTypeOfSymbol(properties[i]));
}
}
const unionType = propTypes.length ? getUnionType(propTypes, /*subtypeReduction*/ true) : undefinedType;
const unionType = propTypes.length ? getUnionType(propTypes, UnionReduction.Subtype) : undefinedType;
return createIndexInfo(unionType, /*isReadonly*/ false);
}
@ -14889,7 +14893,7 @@ namespace ts {
const childrenPropSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, jsxChildrenPropertyName);
childrenPropSymbol.type = childrenTypes.length === 1 ?
childrenTypes[0] :
createArrayType(getUnionType(childrenTypes, /*subtypeReduction*/ false));
createArrayType(getUnionType(childrenTypes));
attributesTable.set(jsxChildrenPropertyName, childrenPropSymbol);
}
}
@ -15025,7 +15029,7 @@ namespace ts {
}
}
return getUnionType(map(instantiatedSignatures, getReturnTypeOfSignature), /*subtypeReduction*/ true);
return getUnionType(map(instantiatedSignatures, getReturnTypeOfSignature), UnionReduction.Subtype);
}
/**
@ -15217,7 +15221,7 @@ namespace ts {
const types = (elementType as UnionType).types;
return getUnionType(types.map(type => {
return resolveCustomJsxElementAttributesType(openingLikeElement, shouldIncludeAllStatelessAttributesType, type, elementClassType);
}), /*subtypeReduction*/ true);
}), UnionReduction.Subtype);
}
// If the elemType is a string type, we have to return anyType to prevent an error downstream as we will try to find construct or call signature of the type
@ -17968,7 +17972,7 @@ namespace ts {
}
// Return a union of the return expression types.
type = getUnionType(types, /*subtypeReduction*/ true);
type = getUnionType(types, UnionReduction.Subtype);
}
if (!contextualSignature) {
@ -18876,7 +18880,7 @@ namespace ts {
leftType;
case SyntaxKind.BarBarToken:
return getTypeFacts(leftType) & TypeFacts.Falsy ?
getUnionType([removeDefinitelyFalsyTypes(leftType), rightType], /*subtypeReduction*/ true) :
getUnionType([removeDefinitelyFalsyTypes(leftType), rightType], UnionReduction.Subtype) :
leftType;
case SyntaxKind.EqualsToken:
checkAssignmentOperator(rightType);
@ -19036,7 +19040,7 @@ namespace ts {
checkExpression(node.condition);
const type1 = checkExpression(node.whenTrue, checkMode);
const type2 = checkExpression(node.whenFalse, checkMode);
return getUnionType([type1, type2], /*subtypeReduction*/ true);
return getUnionType([type1, type2], UnionReduction.Subtype);
}
function checkTemplateExpression(node: TemplateExpression): Type {
@ -20477,7 +20481,7 @@ namespace ts {
return undefined;
}
return typeAsPromise.promisedTypeOfPromise = getUnionType(map(onfulfilledParameterSignatures, getTypeOfFirstParameterOfSignature), /*subtypeReduction*/ true);
return typeAsPromise.promisedTypeOfPromise = getUnionType(map(onfulfilledParameterSignatures, getTypeOfFirstParameterOfSignature), UnionReduction.Subtype);
}
/**
@ -21908,7 +21912,7 @@ namespace ts {
const arrayTypes = (<UnionType>inputType).types;
const filteredTypes = filter(arrayTypes, t => !(t.flags & TypeFlags.StringLike));
if (filteredTypes !== arrayTypes) {
arrayType = getUnionType(filteredTypes, /*subtypeReduction*/ true);
arrayType = getUnionType(filteredTypes, UnionReduction.Subtype);
}
}
else if (arrayType.flags & TypeFlags.StringLike) {
@ -21958,7 +21962,7 @@ namespace ts {
return stringType;
}
return getUnionType([arrayElementType, stringType], /*subtypeReduction*/ true);
return getUnionType([arrayElementType, stringType], UnionReduction.Subtype);
}
return arrayElementType;
@ -22056,7 +22060,7 @@ namespace ts {
return undefined;
}
const returnType = getUnionType(map(signatures, getReturnTypeOfSignature), /*subtypeReduction*/ true);
const returnType = getUnionType(map(signatures, getReturnTypeOfSignature), UnionReduction.Subtype);
const iteratedType = getIteratedTypeOfIterator(returnType, errorNode, /*isAsyncIterator*/ !!asyncMethodType);
if (checkAssignability && errorNode && iteratedType) {
// If `checkAssignability` was specified, we were called from
@ -22131,7 +22135,7 @@ namespace ts {
return undefined;
}
let nextResult = getUnionType(map(nextMethodSignatures, getReturnTypeOfSignature), /*subtypeReduction*/ true);
let nextResult = getUnionType(map(nextMethodSignatures, getReturnTypeOfSignature), UnionReduction.Subtype);
if (isTypeAny(nextResult)) {
return undefined;
}

View file

@ -2787,7 +2787,7 @@ namespace ts {
/* @internal */ getNullType(): Type;
/* @internal */ getESSymbolType(): Type;
/* @internal */ getNeverType(): Type;
/* @internal */ getUnionType(types: Type[], subtypeReduction?: boolean): Type;
/* @internal */ getUnionType(types: Type[], subtypeReduction?: UnionReduction): Type;
/* @internal */ createArrayType(elementType: Type): Type;
/* @internal */ createPromiseType(type: Type): Type;
@ -2842,6 +2842,13 @@ namespace ts {
/* @internal */ getAccessibleSymbolChain(symbol: Symbol, enclosingDeclaration: Node | undefined, meaning: SymbolFlags, useOnlyExternalAliasing: boolean): Symbol[] | undefined;
}
/* @internal */
export const enum UnionReduction {
None = 0,
Literal,
Subtype
}
export enum NodeBuilderFlags {
None = 0,
// Options

View file

@ -305,7 +305,7 @@ namespace ts.codefix {
}
}
if (types.length) {
const type = checker.getWidenedType(checker.getUnionType(types, /*subtypeReduction*/ true));
const type = checker.getWidenedType(checker.getUnionType(types, UnionReduction.Subtype));
paramTypes[parameterIndex] = isRestParameter ? checker.createArrayType(type) : type;
}
}
@ -542,12 +542,12 @@ namespace ts.codefix {
return checker.getStringType();
}
else if (usageContext.candidateTypes) {
return checker.getWidenedType(checker.getUnionType(map(usageContext.candidateTypes, t => checker.getBaseTypeOfLiteralType(t)), /*subtypeReduction*/ true));
return checker.getWidenedType(checker.getUnionType(map(usageContext.candidateTypes, t => checker.getBaseTypeOfLiteralType(t)), UnionReduction.Subtype));
}
else if (usageContext.properties && hasCallContext(usageContext.properties.get("then" as __String))) {
const paramType = getParameterTypeFromCallContexts(0, usageContext.properties.get("then" as __String).callContexts, /*isRestParameter*/ false, checker);
const types = paramType.getCallSignatures().map(c => c.getReturnType());
return checker.createPromiseType(types.length ? checker.getUnionType(types, /*subtypeReduction*/ true) : checker.getAnyType());
return checker.createPromiseType(types.length ? checker.getUnionType(types, UnionReduction.Subtype) : checker.getAnyType());
}
else if (usageContext.properties && hasCallContext(usageContext.properties.get("push" as __String))) {
return checker.createArrayType(getParameterTypeFromCallContexts(0, usageContext.properties.get("push" as __String).callContexts, /*isRestParameter*/ false, checker));
@ -610,7 +610,7 @@ namespace ts.codefix {
}
if (types.length) {
const type = checker.getWidenedType(checker.getUnionType(types, /*subtypeReduction*/ true));
const type = checker.getWidenedType(checker.getUnionType(types, UnionReduction.Subtype));
return isRestParameter ? checker.createArrayType(type) : type;
}
return undefined;

View file

@ -0,0 +1,136 @@
//// [contextualTypeShouldBeLiteral.ts]
interface X {
type: 'x';
value: string;
method(): void;
}
interface Y {
type: 'y';
value: 'none' | 'done';
method(): void;
}
function foo(bar: X | Y) { }
foo({
type: 'y',
value: 'done',
method() {
this;
this.type;
this.value;
}
});
interface X2 {
type1: 'x';
value: string;
method(): void;
}
interface Y2 {
type2: 'y';
value: 'none' | 'done';
method(): void;
}
function foo2(bar: X2 | Y2) { }
foo2({
type2: 'y',
value: 'done',
method() {
this;
this.value;
}
});
interface X3 {
type: 'x';
value: 1 | 2 | 3;
xtra: number;
}
interface Y3 {
type: 'y';
value: 11 | 12 | 13;
ytra: number;
}
let xy: X3 | Y3 = {
type: 'y',
value: 11,
ytra: 12
};
xy;
interface LikeA {
x: 'x';
y: 'y';
value: string;
method(): void;
}
interface LikeB {
x: 'xx';
y: 'yy';
value: number;
method(): void;
}
let xyz: LikeA | LikeB = {
x: 'x',
y: 'y',
value: "foo",
method() {
this;
this.x;
this.y;
this.value;
}
};
xyz;
//// [contextualTypeShouldBeLiteral.js]
"use strict";
function foo(bar) { }
foo({
type: 'y',
value: 'done',
method: function () {
this;
this.type;
this.value;
}
});
function foo2(bar) { }
foo2({
type2: 'y',
value: 'done',
method: function () {
this;
this.value;
}
});
var xy = {
type: 'y',
value: 11,
ytra: 12
};
xy;
var xyz = {
x: 'x',
y: 'y',
value: "foo",
method: function () {
this;
this.x;
this.y;
this.value;
}
};
xyz;

View file

@ -0,0 +1,229 @@
=== tests/cases/compiler/contextualTypeShouldBeLiteral.ts ===
interface X {
>X : Symbol(X, Decl(contextualTypeShouldBeLiteral.ts, 0, 0))
type: 'x';
>type : Symbol(X.type, Decl(contextualTypeShouldBeLiteral.ts, 0, 13))
value: string;
>value : Symbol(X.value, Decl(contextualTypeShouldBeLiteral.ts, 1, 14))
method(): void;
>method : Symbol(X.method, Decl(contextualTypeShouldBeLiteral.ts, 2, 18))
}
interface Y {
>Y : Symbol(Y, Decl(contextualTypeShouldBeLiteral.ts, 4, 1))
type: 'y';
>type : Symbol(Y.type, Decl(contextualTypeShouldBeLiteral.ts, 6, 13))
value: 'none' | 'done';
>value : Symbol(Y.value, Decl(contextualTypeShouldBeLiteral.ts, 7, 14))
method(): void;
>method : Symbol(Y.method, Decl(contextualTypeShouldBeLiteral.ts, 8, 27))
}
function foo(bar: X | Y) { }
>foo : Symbol(foo, Decl(contextualTypeShouldBeLiteral.ts, 10, 1))
>bar : Symbol(bar, Decl(contextualTypeShouldBeLiteral.ts, 12, 13))
>X : Symbol(X, Decl(contextualTypeShouldBeLiteral.ts, 0, 0))
>Y : Symbol(Y, Decl(contextualTypeShouldBeLiteral.ts, 4, 1))
foo({
>foo : Symbol(foo, Decl(contextualTypeShouldBeLiteral.ts, 10, 1))
type: 'y',
>type : Symbol(type, Decl(contextualTypeShouldBeLiteral.ts, 14, 5))
value: 'done',
>value : Symbol(value, Decl(contextualTypeShouldBeLiteral.ts, 15, 14))
method() {
>method : Symbol(method, Decl(contextualTypeShouldBeLiteral.ts, 16, 18))
this;
>this : Symbol(Y, Decl(contextualTypeShouldBeLiteral.ts, 4, 1))
this.type;
>this.type : Symbol(Y.type, Decl(contextualTypeShouldBeLiteral.ts, 6, 13))
>this : Symbol(Y, Decl(contextualTypeShouldBeLiteral.ts, 4, 1))
>type : Symbol(Y.type, Decl(contextualTypeShouldBeLiteral.ts, 6, 13))
this.value;
>this.value : Symbol(Y.value, Decl(contextualTypeShouldBeLiteral.ts, 7, 14))
>this : Symbol(Y, Decl(contextualTypeShouldBeLiteral.ts, 4, 1))
>value : Symbol(Y.value, Decl(contextualTypeShouldBeLiteral.ts, 7, 14))
}
});
interface X2 {
>X2 : Symbol(X2, Decl(contextualTypeShouldBeLiteral.ts, 22, 3))
type1: 'x';
>type1 : Symbol(X2.type1, Decl(contextualTypeShouldBeLiteral.ts, 24, 14))
value: string;
>value : Symbol(X2.value, Decl(contextualTypeShouldBeLiteral.ts, 25, 15))
method(): void;
>method : Symbol(X2.method, Decl(contextualTypeShouldBeLiteral.ts, 26, 18))
}
interface Y2 {
>Y2 : Symbol(Y2, Decl(contextualTypeShouldBeLiteral.ts, 28, 1))
type2: 'y';
>type2 : Symbol(Y2.type2, Decl(contextualTypeShouldBeLiteral.ts, 30, 14))
value: 'none' | 'done';
>value : Symbol(Y2.value, Decl(contextualTypeShouldBeLiteral.ts, 31, 15))
method(): void;
>method : Symbol(Y2.method, Decl(contextualTypeShouldBeLiteral.ts, 32, 27))
}
function foo2(bar: X2 | Y2) { }
>foo2 : Symbol(foo2, Decl(contextualTypeShouldBeLiteral.ts, 34, 1))
>bar : Symbol(bar, Decl(contextualTypeShouldBeLiteral.ts, 36, 14))
>X2 : Symbol(X2, Decl(contextualTypeShouldBeLiteral.ts, 22, 3))
>Y2 : Symbol(Y2, Decl(contextualTypeShouldBeLiteral.ts, 28, 1))
foo2({
>foo2 : Symbol(foo2, Decl(contextualTypeShouldBeLiteral.ts, 34, 1))
type2: 'y',
>type2 : Symbol(type2, Decl(contextualTypeShouldBeLiteral.ts, 38, 6))
value: 'done',
>value : Symbol(value, Decl(contextualTypeShouldBeLiteral.ts, 39, 15))
method() {
>method : Symbol(method, Decl(contextualTypeShouldBeLiteral.ts, 40, 18))
this;
this.value;
>this.value : Symbol(value, Decl(contextualTypeShouldBeLiteral.ts, 25, 15), Decl(contextualTypeShouldBeLiteral.ts, 31, 15))
>value : Symbol(value, Decl(contextualTypeShouldBeLiteral.ts, 25, 15), Decl(contextualTypeShouldBeLiteral.ts, 31, 15))
}
});
interface X3 {
>X3 : Symbol(X3, Decl(contextualTypeShouldBeLiteral.ts, 45, 3))
type: 'x';
>type : Symbol(X3.type, Decl(contextualTypeShouldBeLiteral.ts, 47, 14))
value: 1 | 2 | 3;
>value : Symbol(X3.value, Decl(contextualTypeShouldBeLiteral.ts, 48, 14))
xtra: number;
>xtra : Symbol(X3.xtra, Decl(contextualTypeShouldBeLiteral.ts, 49, 21))
}
interface Y3 {
>Y3 : Symbol(Y3, Decl(contextualTypeShouldBeLiteral.ts, 51, 1))
type: 'y';
>type : Symbol(Y3.type, Decl(contextualTypeShouldBeLiteral.ts, 53, 14))
value: 11 | 12 | 13;
>value : Symbol(Y3.value, Decl(contextualTypeShouldBeLiteral.ts, 54, 14))
ytra: number;
>ytra : Symbol(Y3.ytra, Decl(contextualTypeShouldBeLiteral.ts, 55, 24))
}
let xy: X3 | Y3 = {
>xy : Symbol(xy, Decl(contextualTypeShouldBeLiteral.ts, 59, 3))
>X3 : Symbol(X3, Decl(contextualTypeShouldBeLiteral.ts, 45, 3))
>Y3 : Symbol(Y3, Decl(contextualTypeShouldBeLiteral.ts, 51, 1))
type: 'y',
>type : Symbol(type, Decl(contextualTypeShouldBeLiteral.ts, 59, 19))
value: 11,
>value : Symbol(value, Decl(contextualTypeShouldBeLiteral.ts, 60, 14))
ytra: 12
>ytra : Symbol(ytra, Decl(contextualTypeShouldBeLiteral.ts, 61, 14))
};
xy;
>xy : Symbol(xy, Decl(contextualTypeShouldBeLiteral.ts, 59, 3))
interface LikeA {
>LikeA : Symbol(LikeA, Decl(contextualTypeShouldBeLiteral.ts, 65, 3))
x: 'x';
>x : Symbol(LikeA.x, Decl(contextualTypeShouldBeLiteral.ts, 68, 17))
y: 'y';
>y : Symbol(LikeA.y, Decl(contextualTypeShouldBeLiteral.ts, 69, 11))
value: string;
>value : Symbol(LikeA.value, Decl(contextualTypeShouldBeLiteral.ts, 70, 11))
method(): void;
>method : Symbol(LikeA.method, Decl(contextualTypeShouldBeLiteral.ts, 71, 18))
}
interface LikeB {
>LikeB : Symbol(LikeB, Decl(contextualTypeShouldBeLiteral.ts, 73, 1))
x: 'xx';
>x : Symbol(LikeB.x, Decl(contextualTypeShouldBeLiteral.ts, 75, 17))
y: 'yy';
>y : Symbol(LikeB.y, Decl(contextualTypeShouldBeLiteral.ts, 76, 12))
value: number;
>value : Symbol(LikeB.value, Decl(contextualTypeShouldBeLiteral.ts, 77, 12))
method(): void;
>method : Symbol(LikeB.method, Decl(contextualTypeShouldBeLiteral.ts, 78, 18))
}
let xyz: LikeA | LikeB = {
>xyz : Symbol(xyz, Decl(contextualTypeShouldBeLiteral.ts, 82, 3))
>LikeA : Symbol(LikeA, Decl(contextualTypeShouldBeLiteral.ts, 65, 3))
>LikeB : Symbol(LikeB, Decl(contextualTypeShouldBeLiteral.ts, 73, 1))
x: 'x',
>x : Symbol(x, Decl(contextualTypeShouldBeLiteral.ts, 82, 26))
y: 'y',
>y : Symbol(y, Decl(contextualTypeShouldBeLiteral.ts, 83, 11))
value: "foo",
>value : Symbol(value, Decl(contextualTypeShouldBeLiteral.ts, 84, 11))
method() {
>method : Symbol(method, Decl(contextualTypeShouldBeLiteral.ts, 85, 17))
this;
>this : Symbol(LikeA, Decl(contextualTypeShouldBeLiteral.ts, 65, 3))
this.x;
>this.x : Symbol(LikeA.x, Decl(contextualTypeShouldBeLiteral.ts, 68, 17))
>this : Symbol(LikeA, Decl(contextualTypeShouldBeLiteral.ts, 65, 3))
>x : Symbol(LikeA.x, Decl(contextualTypeShouldBeLiteral.ts, 68, 17))
this.y;
>this.y : Symbol(LikeA.y, Decl(contextualTypeShouldBeLiteral.ts, 69, 11))
>this : Symbol(LikeA, Decl(contextualTypeShouldBeLiteral.ts, 65, 3))
>y : Symbol(LikeA.y, Decl(contextualTypeShouldBeLiteral.ts, 69, 11))
this.value;
>this.value : Symbol(LikeA.value, Decl(contextualTypeShouldBeLiteral.ts, 70, 11))
>this : Symbol(LikeA, Decl(contextualTypeShouldBeLiteral.ts, 65, 3))
>value : Symbol(LikeA.value, Decl(contextualTypeShouldBeLiteral.ts, 70, 11))
}
};
xyz;
>xyz : Symbol(xyz, Decl(contextualTypeShouldBeLiteral.ts, 82, 3))

View file

@ -0,0 +1,248 @@
=== tests/cases/compiler/contextualTypeShouldBeLiteral.ts ===
interface X {
>X : X
type: 'x';
>type : "x"
value: string;
>value : string
method(): void;
>method : () => void
}
interface Y {
>Y : Y
type: 'y';
>type : "y"
value: 'none' | 'done';
>value : "none" | "done"
method(): void;
>method : () => void
}
function foo(bar: X | Y) { }
>foo : (bar: X | Y) => void
>bar : X | Y
>X : X
>Y : Y
foo({
>foo({ type: 'y', value: 'done', method() { this; this.type; this.value; }}) : void
>foo : (bar: X | Y) => void
>{ type: 'y', value: 'done', method() { this; this.type; this.value; }} : { type: "y"; value: "done"; method(): void; }
type: 'y',
>type : string
>'y' : "y"
value: 'done',
>value : string
>'done' : "done"
method() {
>method : () => void
this;
>this : Y
this.type;
>this.type : "y"
>this : Y
>type : "y"
this.value;
>this.value : "none" | "done"
>this : Y
>value : "none" | "done"
}
});
interface X2 {
>X2 : X2
type1: 'x';
>type1 : "x"
value: string;
>value : string
method(): void;
>method : () => void
}
interface Y2 {
>Y2 : Y2
type2: 'y';
>type2 : "y"
value: 'none' | 'done';
>value : "none" | "done"
method(): void;
>method : () => void
}
function foo2(bar: X2 | Y2) { }
>foo2 : (bar: X2 | Y2) => void
>bar : X2 | Y2
>X2 : X2
>Y2 : Y2
foo2({
>foo2({ type2: 'y', value: 'done', method() { this; this.value; }}) : void
>foo2 : (bar: X2 | Y2) => void
>{ type2: 'y', value: 'done', method() { this; this.value; }} : { type2: "y"; value: "done"; method(): void; }
type2: 'y',
>type2 : string
>'y' : "y"
value: 'done',
>value : string
>'done' : "done"
method() {
>method : () => void
this;
>this : X2 | Y2
this.value;
>this.value : string
>this : X2 | Y2
>value : string
}
});
interface X3 {
>X3 : X3
type: 'x';
>type : "x"
value: 1 | 2 | 3;
>value : 1 | 2 | 3
xtra: number;
>xtra : number
}
interface Y3 {
>Y3 : Y3
type: 'y';
>type : "y"
value: 11 | 12 | 13;
>value : 11 | 12 | 13
ytra: number;
>ytra : number
}
let xy: X3 | Y3 = {
>xy : X3 | Y3
>X3 : X3
>Y3 : Y3
>{ type: 'y', value: 11, ytra: 12} : { type: "y"; value: 11; ytra: number; }
type: 'y',
>type : string
>'y' : "y"
value: 11,
>value : number
>11 : 11
ytra: 12
>ytra : number
>12 : 12
};
xy;
>xy : Y3
interface LikeA {
>LikeA : LikeA
x: 'x';
>x : "x"
y: 'y';
>y : "y"
value: string;
>value : string
method(): void;
>method : () => void
}
interface LikeB {
>LikeB : LikeB
x: 'xx';
>x : "xx"
y: 'yy';
>y : "yy"
value: number;
>value : number
method(): void;
>method : () => void
}
let xyz: LikeA | LikeB = {
>xyz : LikeA | LikeB
>LikeA : LikeA
>LikeB : LikeB
>{ x: 'x', y: 'y', value: "foo", method() { this; this.x; this.y; this.value; }} : { x: "x"; y: "y"; value: string; method(): void; }
x: 'x',
>x : string
>'x' : "x"
y: 'y',
>y : string
>'y' : "y"
value: "foo",
>value : string
>"foo" : "foo"
method() {
>method : () => void
this;
>this : LikeA
this.x;
>this.x : "x"
>this : LikeA
>x : "x"
this.y;
>this.y : "y"
>this : LikeA
>y : "y"
this.value;
>this.value : string
>this : LikeA
>value : string
}
};
xyz;
>xyz : LikeA

View file

@ -0,0 +1,32 @@
//// [nestedTypeVariableInfersLiteral.ts]
// https://github.com/Microsoft/TypeScript/issues/19632
declare function direct<A extends string>(a: A | A[]): Record<A, string>
declare function nested<A extends string>(a: { fields: A }): Record<A, string>
declare function nestedUnion<A extends string>(a: { fields: A | A[] }): Record<A, string>
const directUnionSingle = direct("z")
const directUnionArray = direct(["z", "y"])
const nestedSingle = nested({fields: "z"})
const nestedUnionSingle = nestedUnion({fields: "z"})
const nestedUnionArray = nestedUnion({fields: ["z", "y"]})
declare function hasZField(arg: { z: string }): void
hasZField(directUnionSingle) // ok
hasZField(directUnionArray) // ok
hasZField(nestedSingle) // ok
hasZField(nestedUnionSingle) // ok
hasZField(nestedUnionArray) // ok
//// [nestedTypeVariableInfersLiteral.js]
var directUnionSingle = direct("z");
var directUnionArray = direct(["z", "y"]);
var nestedSingle = nested({ fields: "z" });
var nestedUnionSingle = nestedUnion({ fields: "z" });
var nestedUnionArray = nestedUnion({ fields: ["z", "y"] });
hasZField(directUnionSingle); // ok
hasZField(directUnionArray); // ok
hasZField(nestedSingle); // ok
hasZField(nestedUnionSingle); // ok
hasZField(nestedUnionArray); // ok

View file

@ -0,0 +1,78 @@
=== tests/cases/compiler/nestedTypeVariableInfersLiteral.ts ===
// https://github.com/Microsoft/TypeScript/issues/19632
declare function direct<A extends string>(a: A | A[]): Record<A, string>
>direct : Symbol(direct, Decl(nestedTypeVariableInfersLiteral.ts, 0, 0))
>A : Symbol(A, Decl(nestedTypeVariableInfersLiteral.ts, 1, 24))
>a : Symbol(a, Decl(nestedTypeVariableInfersLiteral.ts, 1, 42))
>A : Symbol(A, Decl(nestedTypeVariableInfersLiteral.ts, 1, 24))
>A : Symbol(A, Decl(nestedTypeVariableInfersLiteral.ts, 1, 24))
>Record : Symbol(Record, Decl(lib.d.ts, --, --))
>A : Symbol(A, Decl(nestedTypeVariableInfersLiteral.ts, 1, 24))
declare function nested<A extends string>(a: { fields: A }): Record<A, string>
>nested : Symbol(nested, Decl(nestedTypeVariableInfersLiteral.ts, 1, 72))
>A : Symbol(A, Decl(nestedTypeVariableInfersLiteral.ts, 2, 24))
>a : Symbol(a, Decl(nestedTypeVariableInfersLiteral.ts, 2, 42))
>fields : Symbol(fields, Decl(nestedTypeVariableInfersLiteral.ts, 2, 46))
>A : Symbol(A, Decl(nestedTypeVariableInfersLiteral.ts, 2, 24))
>Record : Symbol(Record, Decl(lib.d.ts, --, --))
>A : Symbol(A, Decl(nestedTypeVariableInfersLiteral.ts, 2, 24))
declare function nestedUnion<A extends string>(a: { fields: A | A[] }): Record<A, string>
>nestedUnion : Symbol(nestedUnion, Decl(nestedTypeVariableInfersLiteral.ts, 2, 78))
>A : Symbol(A, Decl(nestedTypeVariableInfersLiteral.ts, 3, 29))
>a : Symbol(a, Decl(nestedTypeVariableInfersLiteral.ts, 3, 47))
>fields : Symbol(fields, Decl(nestedTypeVariableInfersLiteral.ts, 3, 51))
>A : Symbol(A, Decl(nestedTypeVariableInfersLiteral.ts, 3, 29))
>A : Symbol(A, Decl(nestedTypeVariableInfersLiteral.ts, 3, 29))
>Record : Symbol(Record, Decl(lib.d.ts, --, --))
>A : Symbol(A, Decl(nestedTypeVariableInfersLiteral.ts, 3, 29))
const directUnionSingle = direct("z")
>directUnionSingle : Symbol(directUnionSingle, Decl(nestedTypeVariableInfersLiteral.ts, 5, 5))
>direct : Symbol(direct, Decl(nestedTypeVariableInfersLiteral.ts, 0, 0))
const directUnionArray = direct(["z", "y"])
>directUnionArray : Symbol(directUnionArray, Decl(nestedTypeVariableInfersLiteral.ts, 6, 5))
>direct : Symbol(direct, Decl(nestedTypeVariableInfersLiteral.ts, 0, 0))
const nestedSingle = nested({fields: "z"})
>nestedSingle : Symbol(nestedSingle, Decl(nestedTypeVariableInfersLiteral.ts, 7, 5))
>nested : Symbol(nested, Decl(nestedTypeVariableInfersLiteral.ts, 1, 72))
>fields : Symbol(fields, Decl(nestedTypeVariableInfersLiteral.ts, 7, 29))
const nestedUnionSingle = nestedUnion({fields: "z"})
>nestedUnionSingle : Symbol(nestedUnionSingle, Decl(nestedTypeVariableInfersLiteral.ts, 8, 5))
>nestedUnion : Symbol(nestedUnion, Decl(nestedTypeVariableInfersLiteral.ts, 2, 78))
>fields : Symbol(fields, Decl(nestedTypeVariableInfersLiteral.ts, 8, 39))
const nestedUnionArray = nestedUnion({fields: ["z", "y"]})
>nestedUnionArray : Symbol(nestedUnionArray, Decl(nestedTypeVariableInfersLiteral.ts, 9, 5))
>nestedUnion : Symbol(nestedUnion, Decl(nestedTypeVariableInfersLiteral.ts, 2, 78))
>fields : Symbol(fields, Decl(nestedTypeVariableInfersLiteral.ts, 9, 38))
declare function hasZField(arg: { z: string }): void
>hasZField : Symbol(hasZField, Decl(nestedTypeVariableInfersLiteral.ts, 9, 58))
>arg : Symbol(arg, Decl(nestedTypeVariableInfersLiteral.ts, 11, 27))
>z : Symbol(z, Decl(nestedTypeVariableInfersLiteral.ts, 11, 33))
hasZField(directUnionSingle) // ok
>hasZField : Symbol(hasZField, Decl(nestedTypeVariableInfersLiteral.ts, 9, 58))
>directUnionSingle : Symbol(directUnionSingle, Decl(nestedTypeVariableInfersLiteral.ts, 5, 5))
hasZField(directUnionArray) // ok
>hasZField : Symbol(hasZField, Decl(nestedTypeVariableInfersLiteral.ts, 9, 58))
>directUnionArray : Symbol(directUnionArray, Decl(nestedTypeVariableInfersLiteral.ts, 6, 5))
hasZField(nestedSingle) // ok
>hasZField : Symbol(hasZField, Decl(nestedTypeVariableInfersLiteral.ts, 9, 58))
>nestedSingle : Symbol(nestedSingle, Decl(nestedTypeVariableInfersLiteral.ts, 7, 5))
hasZField(nestedUnionSingle) // ok
>hasZField : Symbol(hasZField, Decl(nestedTypeVariableInfersLiteral.ts, 9, 58))
>nestedUnionSingle : Symbol(nestedUnionSingle, Decl(nestedTypeVariableInfersLiteral.ts, 8, 5))
hasZField(nestedUnionArray) // ok
>hasZField : Symbol(hasZField, Decl(nestedTypeVariableInfersLiteral.ts, 9, 58))
>nestedUnionArray : Symbol(nestedUnionArray, Decl(nestedTypeVariableInfersLiteral.ts, 9, 5))

View file

@ -0,0 +1,100 @@
=== tests/cases/compiler/nestedTypeVariableInfersLiteral.ts ===
// https://github.com/Microsoft/TypeScript/issues/19632
declare function direct<A extends string>(a: A | A[]): Record<A, string>
>direct : <A extends string>(a: A | A[]) => Record<A, string>
>A : A
>a : A | A[]
>A : A
>A : A
>Record : Record<K, T>
>A : A
declare function nested<A extends string>(a: { fields: A }): Record<A, string>
>nested : <A extends string>(a: { fields: A; }) => Record<A, string>
>A : A
>a : { fields: A; }
>fields : A
>A : A
>Record : Record<K, T>
>A : A
declare function nestedUnion<A extends string>(a: { fields: A | A[] }): Record<A, string>
>nestedUnion : <A extends string>(a: { fields: A | A[]; }) => Record<A, string>
>A : A
>a : { fields: A | A[]; }
>fields : A | A[]
>A : A
>A : A
>Record : Record<K, T>
>A : A
const directUnionSingle = direct("z")
>directUnionSingle : Record<"z", string>
>direct("z") : Record<"z", string>
>direct : <A extends string>(a: A | A[]) => Record<A, string>
>"z" : "z"
const directUnionArray = direct(["z", "y"])
>directUnionArray : Record<"z" | "y", string>
>direct(["z", "y"]) : Record<"z" | "y", string>
>direct : <A extends string>(a: A | A[]) => Record<A, string>
>["z", "y"] : ("z" | "y")[]
>"z" : "z"
>"y" : "y"
const nestedSingle = nested({fields: "z"})
>nestedSingle : Record<"z", string>
>nested({fields: "z"}) : Record<"z", string>
>nested : <A extends string>(a: { fields: A; }) => Record<A, string>
>{fields: "z"} : { fields: "z"; }
>fields : string
>"z" : "z"
const nestedUnionSingle = nestedUnion({fields: "z"})
>nestedUnionSingle : Record<"z", string>
>nestedUnion({fields: "z"}) : Record<"z", string>
>nestedUnion : <A extends string>(a: { fields: A | A[]; }) => Record<A, string>
>{fields: "z"} : { fields: "z"; }
>fields : string
>"z" : "z"
const nestedUnionArray = nestedUnion({fields: ["z", "y"]})
>nestedUnionArray : Record<"z" | "y", string>
>nestedUnion({fields: ["z", "y"]}) : Record<"z" | "y", string>
>nestedUnion : <A extends string>(a: { fields: A | A[]; }) => Record<A, string>
>{fields: ["z", "y"]} : { fields: ("z" | "y")[]; }
>fields : ("z" | "y")[]
>["z", "y"] : ("z" | "y")[]
>"z" : "z"
>"y" : "y"
declare function hasZField(arg: { z: string }): void
>hasZField : (arg: { z: string; }) => void
>arg : { z: string; }
>z : string
hasZField(directUnionSingle) // ok
>hasZField(directUnionSingle) : void
>hasZField : (arg: { z: string; }) => void
>directUnionSingle : Record<"z", string>
hasZField(directUnionArray) // ok
>hasZField(directUnionArray) : void
>hasZField : (arg: { z: string; }) => void
>directUnionArray : Record<"z" | "y", string>
hasZField(nestedSingle) // ok
>hasZField(nestedSingle) : void
>hasZField : (arg: { z: string; }) => void
>nestedSingle : Record<"z", string>
hasZField(nestedUnionSingle) // ok
>hasZField(nestedUnionSingle) : void
>hasZField : (arg: { z: string; }) => void
>nestedUnionSingle : Record<"z", string>
hasZField(nestedUnionArray) // ok
>hasZField(nestedUnionArray) : void
>hasZField : (arg: { z: string; }) => void
>nestedUnionArray : Record<"z" | "y", string>

View file

@ -0,0 +1,96 @@
// @strict: true
interface X {
type: 'x';
value: string;
method(): void;
}
interface Y {
type: 'y';
value: 'none' | 'done';
method(): void;
}
function foo(bar: X | Y) { }
foo({
type: 'y',
value: 'done',
method() {
this;
this.type;
this.value;
}
});
interface X2 {
type1: 'x';
value: string;
method(): void;
}
interface Y2 {
type2: 'y';
value: 'none' | 'done';
method(): void;
}
function foo2(bar: X2 | Y2) { }
foo2({
type2: 'y',
value: 'done',
method() {
this;
this.value;
}
});
interface X3 {
type: 'x';
value: 1 | 2 | 3;
xtra: number;
}
interface Y3 {
type: 'y';
value: 11 | 12 | 13;
ytra: number;
}
let xy: X3 | Y3 = {
type: 'y',
value: 11,
ytra: 12
};
xy;
interface LikeA {
x: 'x';
y: 'y';
value: string;
method(): void;
}
interface LikeB {
x: 'xx';
y: 'yy';
value: number;
method(): void;
}
let xyz: LikeA | LikeB = {
x: 'x',
y: 'y',
value: "foo",
method() {
this;
this.x;
this.y;
this.value;
}
};
xyz;

View file

@ -0,0 +1,18 @@
// https://github.com/Microsoft/TypeScript/issues/19632
declare function direct<A extends string>(a: A | A[]): Record<A, string>
declare function nested<A extends string>(a: { fields: A }): Record<A, string>
declare function nestedUnion<A extends string>(a: { fields: A | A[] }): Record<A, string>
const directUnionSingle = direct("z")
const directUnionArray = direct(["z", "y"])
const nestedSingle = nested({fields: "z"})
const nestedUnionSingle = nestedUnion({fields: "z"})
const nestedUnionArray = nestedUnion({fields: ["z", "y"]})
declare function hasZField(arg: { z: string }): void
hasZField(directUnionSingle) // ok
hasZField(directUnionArray) // ok
hasZField(nestedSingle) // ok
hasZField(nestedUnionSingle) // ok
hasZField(nestedUnionArray) // ok

@ -1 +1 @@
Subproject commit ed149eb0c787b1195a95b44105822c64bb6eb636
Subproject commit 9c8f67f596e23283f7fe452d67372233d2e4e5d6

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,6 @@
{
"compilerOptions": {
"target": "es6",
"types": []
}
}