Merge pull request #28005 from Microsoft/noReductionUnion
Do not do any reduction (even if it contains any) to the union type when getting contextual type
This commit is contained in:
commit
424fcdde37
5 changed files with 90 additions and 29 deletions
|
@ -8855,10 +8855,10 @@ namespace ts {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function addTypeToUnion(typeSet: Type[], includes: TypeFlags, type: Type) {
|
function addTypeToUnion(typeSet: Type[], includes: TypeFlags, type: Type, noReduction: boolean) {
|
||||||
const flags = type.flags;
|
const flags = type.flags;
|
||||||
if (flags & TypeFlags.Union) {
|
if (flags & TypeFlags.Union) {
|
||||||
return addTypesToUnion(typeSet, includes, (<UnionType>type).types);
|
return addTypesToUnion(typeSet, includes, (<UnionType>type).types, noReduction);
|
||||||
}
|
}
|
||||||
// We ignore 'never' types in unions. Likewise, we ignore intersections of unit types as they are
|
// We ignore 'never' types in unions. Likewise, we ignore intersections of unit types as they are
|
||||||
// another form of 'never' (in that they have an empty value domain). We could in theory turn
|
// another form of 'never' (in that they have an empty value domain). We could in theory turn
|
||||||
|
@ -8866,28 +8866,35 @@ namespace ts {
|
||||||
// easier to reason about their origin.
|
// easier to reason about their origin.
|
||||||
if (!(flags & TypeFlags.Never || flags & TypeFlags.Intersection && isEmptyIntersectionType(<IntersectionType>type))) {
|
if (!(flags & TypeFlags.Never || flags & TypeFlags.Intersection && isEmptyIntersectionType(<IntersectionType>type))) {
|
||||||
includes |= flags & ~TypeFlags.ConstructionFlags;
|
includes |= flags & ~TypeFlags.ConstructionFlags;
|
||||||
if (flags & TypeFlags.AnyOrUnknown) {
|
if (noReduction) {
|
||||||
|
addTypeToTypeSet(typeSet, type);
|
||||||
|
}
|
||||||
|
else if (flags & TypeFlags.AnyOrUnknown) {
|
||||||
if (type === wildcardType) includes |= TypeFlags.Wildcard;
|
if (type === wildcardType) includes |= TypeFlags.Wildcard;
|
||||||
}
|
}
|
||||||
else if (!strictNullChecks && flags & TypeFlags.Nullable) {
|
else if (!strictNullChecks && flags & TypeFlags.Nullable) {
|
||||||
if (!(flags & TypeFlags.ContainsWideningType)) includes |= TypeFlags.NonWideningType;
|
if (!(flags & TypeFlags.ContainsWideningType)) includes |= TypeFlags.NonWideningType;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const len = typeSet.length;
|
addTypeToTypeSet(typeSet, type);
|
||||||
const index = len && type.id > typeSet[len - 1].id ? ~len : binarySearch(typeSet, type, getTypeId, compareValues);
|
|
||||||
if (index < 0) {
|
|
||||||
typeSet.splice(~index, 0, type);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return includes;
|
return includes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addTypeToTypeSet(typeSet: Type[], type: Type) {
|
||||||
|
const len = typeSet.length;
|
||||||
|
const index = len && type.id > typeSet[len - 1].id ? ~len : binarySearch(typeSet, type, getTypeId, compareValues);
|
||||||
|
if (index < 0) {
|
||||||
|
typeSet.splice(~index, 0, type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Add the given types to the given type set. Order is preserved, duplicates are removed,
|
// Add the given types to the given type set. Order is preserved, duplicates are removed,
|
||||||
// and nested types of the given kind are flattened into the set.
|
// and nested types of the given kind are flattened into the set.
|
||||||
function addTypesToUnion(typeSet: Type[], includes: TypeFlags, types: ReadonlyArray<Type>): TypeFlags {
|
function addTypesToUnion(typeSet: Type[], includes: TypeFlags, types: ReadonlyArray<Type>, noReduction: boolean): TypeFlags {
|
||||||
for (const type of types) {
|
for (const type of types) {
|
||||||
includes = addTypeToUnion(typeSet, includes, type);
|
includes = addTypeToUnion(typeSet, includes, type, noReduction);
|
||||||
}
|
}
|
||||||
return includes;
|
return includes;
|
||||||
}
|
}
|
||||||
|
@ -8964,24 +8971,26 @@ namespace ts {
|
||||||
return types[0];
|
return types[0];
|
||||||
}
|
}
|
||||||
const typeSet: Type[] = [];
|
const typeSet: Type[] = [];
|
||||||
const includes = addTypesToUnion(typeSet, 0, types);
|
const includes = addTypesToUnion(typeSet, 0, types, unionReduction === UnionReduction.None);
|
||||||
if (includes & TypeFlags.AnyOrUnknown) {
|
if (unionReduction !== UnionReduction.None) {
|
||||||
return includes & TypeFlags.Any ? includes & TypeFlags.Wildcard ? wildcardType : anyType : unknownType;
|
if (includes & TypeFlags.AnyOrUnknown) {
|
||||||
}
|
return includes & TypeFlags.Any ? includes & TypeFlags.Wildcard ? wildcardType : anyType : unknownType;
|
||||||
switch (unionReduction) {
|
}
|
||||||
case UnionReduction.Literal:
|
switch (unionReduction) {
|
||||||
if (includes & TypeFlags.StringOrNumberLiteralOrUnique | TypeFlags.BooleanLiteral) {
|
case UnionReduction.Literal:
|
||||||
removeRedundantLiteralTypes(typeSet, includes);
|
if (includes & TypeFlags.StringOrNumberLiteralOrUnique | TypeFlags.BooleanLiteral) {
|
||||||
}
|
removeRedundantLiteralTypes(typeSet, includes);
|
||||||
break;
|
}
|
||||||
case UnionReduction.Subtype:
|
break;
|
||||||
removeSubtypes(typeSet);
|
case UnionReduction.Subtype:
|
||||||
break;
|
removeSubtypes(typeSet);
|
||||||
}
|
break;
|
||||||
if (typeSet.length === 0) {
|
}
|
||||||
return includes & TypeFlags.Null ? includes & TypeFlags.NonWideningType ? nullType : nullWideningType :
|
if (typeSet.length === 0) {
|
||||||
includes & TypeFlags.Undefined ? includes & TypeFlags.NonWideningType ? undefinedType : undefinedWideningType :
|
return includes & TypeFlags.Null ? includes & TypeFlags.NonWideningType ? nullType : nullWideningType :
|
||||||
neverType;
|
includes & TypeFlags.Undefined ? includes & TypeFlags.NonWideningType ? undefinedType : undefinedWideningType :
|
||||||
|
neverType;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return getUnionTypeFromSortedList(typeSet, includes & TypeFlags.NotPrimitiveUnion ? 0 : TypeFlags.UnionOfPrimitiveTypes, aliasSymbol, aliasTypeArguments);
|
return getUnionTypeFromSortedList(typeSet, includes & TypeFlags.NotPrimitiveUnion ? 0 : TypeFlags.UnionOfPrimitiveTypes, aliasSymbol, aliasTypeArguments);
|
||||||
}
|
}
|
||||||
|
@ -16868,7 +16877,6 @@ namespace ts {
|
||||||
function getContextualTypeForElementExpression(arrayContextualType: Type | undefined, index: number): Type | undefined {
|
function getContextualTypeForElementExpression(arrayContextualType: Type | undefined, index: number): Type | undefined {
|
||||||
return arrayContextualType && (
|
return arrayContextualType && (
|
||||||
getTypeOfPropertyOfContextualType(arrayContextualType, "" + index as __String)
|
getTypeOfPropertyOfContextualType(arrayContextualType, "" + index as __String)
|
||||||
|| getIndexTypeOfContextualType(arrayContextualType, IndexKind.Number)
|
|
||||||
|| getIteratedTypeOrElementType(arrayContextualType, /*errorNode*/ undefined, /*allowStringInput*/ false, /*allowAsyncIterables*/ false, /*checkAssignability*/ false));
|
|| getIteratedTypeOrElementType(arrayContextualType, /*errorNode*/ undefined, /*allowStringInput*/ false, /*allowAsyncIterables*/ false, /*checkAssignability*/ false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
11
tests/baselines/reference/unionTypeWithIndexAndTuple.js
Normal file
11
tests/baselines/reference/unionTypeWithIndexAndTuple.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
//// [unionTypeWithIndexAndTuple.ts]
|
||||||
|
interface I {
|
||||||
|
[index: number]: any;
|
||||||
|
someOtherProperty: number;
|
||||||
|
}
|
||||||
|
function f(args: ["a"] | I) { }
|
||||||
|
f(["a"]);
|
||||||
|
|
||||||
|
//// [unionTypeWithIndexAndTuple.js]
|
||||||
|
function f(args) { }
|
||||||
|
f(["a"]);
|
18
tests/baselines/reference/unionTypeWithIndexAndTuple.symbols
Normal file
18
tests/baselines/reference/unionTypeWithIndexAndTuple.symbols
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
=== tests/cases/compiler/unionTypeWithIndexAndTuple.ts ===
|
||||||
|
interface I {
|
||||||
|
>I : Symbol(I, Decl(unionTypeWithIndexAndTuple.ts, 0, 0))
|
||||||
|
|
||||||
|
[index: number]: any;
|
||||||
|
>index : Symbol(index, Decl(unionTypeWithIndexAndTuple.ts, 1, 5))
|
||||||
|
|
||||||
|
someOtherProperty: number;
|
||||||
|
>someOtherProperty : Symbol(I.someOtherProperty, Decl(unionTypeWithIndexAndTuple.ts, 1, 25))
|
||||||
|
}
|
||||||
|
function f(args: ["a"] | I) { }
|
||||||
|
>f : Symbol(f, Decl(unionTypeWithIndexAndTuple.ts, 3, 1))
|
||||||
|
>args : Symbol(args, Decl(unionTypeWithIndexAndTuple.ts, 4, 11))
|
||||||
|
>I : Symbol(I, Decl(unionTypeWithIndexAndTuple.ts, 0, 0))
|
||||||
|
|
||||||
|
f(["a"]);
|
||||||
|
>f : Symbol(f, Decl(unionTypeWithIndexAndTuple.ts, 3, 1))
|
||||||
|
|
18
tests/baselines/reference/unionTypeWithIndexAndTuple.types
Normal file
18
tests/baselines/reference/unionTypeWithIndexAndTuple.types
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
=== tests/cases/compiler/unionTypeWithIndexAndTuple.ts ===
|
||||||
|
interface I {
|
||||||
|
[index: number]: any;
|
||||||
|
>index : number
|
||||||
|
|
||||||
|
someOtherProperty: number;
|
||||||
|
>someOtherProperty : number
|
||||||
|
}
|
||||||
|
function f(args: ["a"] | I) { }
|
||||||
|
>f : (args: I | ["a"]) => void
|
||||||
|
>args : I | ["a"]
|
||||||
|
|
||||||
|
f(["a"]);
|
||||||
|
>f(["a"]) : void
|
||||||
|
>f : (args: I | ["a"]) => void
|
||||||
|
>["a"] : ["a"]
|
||||||
|
>"a" : "a"
|
||||||
|
|
6
tests/cases/compiler/unionTypeWithIndexAndTuple.ts
Normal file
6
tests/cases/compiler/unionTypeWithIndexAndTuple.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
interface I {
|
||||||
|
[index: number]: any;
|
||||||
|
someOtherProperty: number;
|
||||||
|
}
|
||||||
|
function f(args: ["a"] | I) { }
|
||||||
|
f(["a"]);
|
Loading…
Reference in a new issue