Make substitution types even if the substitution base isnt a type variable (#37348)
* Make substitution types even if the substitution base isnt a type variable * Broaden usage of getConditionalFlowTypeOfType to _all_ type lookups * Align comments
This commit is contained in:
parent
db44231e38
commit
47b60ece0b
|
@ -4278,7 +4278,7 @@ namespace ts {
|
||||||
return createConditionalTypeNode(checkTypeNode, extendsTypeNode, trueTypeNode, falseTypeNode);
|
return createConditionalTypeNode(checkTypeNode, extendsTypeNode, trueTypeNode, falseTypeNode);
|
||||||
}
|
}
|
||||||
if (type.flags & TypeFlags.Substitution) {
|
if (type.flags & TypeFlags.Substitution) {
|
||||||
return typeToTypeNodeHelper((<SubstitutionType>type).typeVariable, context);
|
return typeToTypeNodeHelper((<SubstitutionType>type).baseType, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Debug.fail("Should be unreachable.");
|
return Debug.fail("Should be unreachable.");
|
||||||
|
@ -11327,9 +11327,7 @@ namespace ts {
|
||||||
// Get type from reference to named type that cannot be generic (enum or type parameter)
|
// Get type from reference to named type that cannot be generic (enum or type parameter)
|
||||||
const res = tryGetDeclaredTypeOfSymbol(symbol);
|
const res = tryGetDeclaredTypeOfSymbol(symbol);
|
||||||
if (res) {
|
if (res) {
|
||||||
return checkNoTypeArguments(node, symbol) ?
|
return checkNoTypeArguments(node, symbol) ? getRegularTypeOfLiteralType(res) : errorType;
|
||||||
res.flags & TypeFlags.TypeParameter ? getConstrainedTypeVariable(<TypeParameter>res, node) : getRegularTypeOfLiteralType(res) :
|
|
||||||
errorType;
|
|
||||||
}
|
}
|
||||||
if (symbol.flags & SymbolFlags.Value && isJSDocTypeReference(node)) {
|
if (symbol.flags & SymbolFlags.Value && isJSDocTypeReference(node)) {
|
||||||
const jsdocType = getTypeFromJSDocValueReference(node, symbol);
|
const jsdocType = getTypeFromJSDocValueReference(node, symbol);
|
||||||
|
@ -11377,17 +11375,17 @@ namespace ts {
|
||||||
return links.resolvedJSDocType;
|
return links.resolvedJSDocType;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSubstitutionType(typeVariable: TypeVariable, substitute: Type) {
|
function getSubstitutionType(baseType: Type, substitute: Type) {
|
||||||
if (substitute.flags & TypeFlags.AnyOrUnknown || substitute === typeVariable) {
|
if (substitute.flags & TypeFlags.AnyOrUnknown || substitute === baseType) {
|
||||||
return typeVariable;
|
return baseType;
|
||||||
}
|
}
|
||||||
const id = `${getTypeId(typeVariable)}>${getTypeId(substitute)}`;
|
const id = `${getTypeId(baseType)}>${getTypeId(substitute)}`;
|
||||||
const cached = substitutionTypes.get(id);
|
const cached = substitutionTypes.get(id);
|
||||||
if (cached) {
|
if (cached) {
|
||||||
return cached;
|
return cached;
|
||||||
}
|
}
|
||||||
const result = <SubstitutionType>createType(TypeFlags.Substitution);
|
const result = <SubstitutionType>createType(TypeFlags.Substitution);
|
||||||
result.typeVariable = typeVariable;
|
result.baseType = baseType;
|
||||||
result.substitute = substitute;
|
result.substitute = substitute;
|
||||||
substitutionTypes.set(id, result);
|
substitutionTypes.set(id, result);
|
||||||
return result;
|
return result;
|
||||||
|
@ -11397,25 +11395,25 @@ namespace ts {
|
||||||
return node.kind === SyntaxKind.TupleType && (<TupleTypeNode>node).elementTypes.length === 1;
|
return node.kind === SyntaxKind.TupleType && (<TupleTypeNode>node).elementTypes.length === 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getImpliedConstraint(typeVariable: TypeVariable, checkNode: TypeNode, extendsNode: TypeNode): Type | undefined {
|
function getImpliedConstraint(type: Type, checkNode: TypeNode, extendsNode: TypeNode): Type | undefined {
|
||||||
return isUnaryTupleTypeNode(checkNode) && isUnaryTupleTypeNode(extendsNode) ? getImpliedConstraint(typeVariable, (<TupleTypeNode>checkNode).elementTypes[0], (<TupleTypeNode>extendsNode).elementTypes[0]) :
|
return isUnaryTupleTypeNode(checkNode) && isUnaryTupleTypeNode(extendsNode) ? getImpliedConstraint(type, (<TupleTypeNode>checkNode).elementTypes[0], (<TupleTypeNode>extendsNode).elementTypes[0]) :
|
||||||
getActualTypeVariable(getTypeFromTypeNode(checkNode)) === typeVariable ? getTypeFromTypeNode(extendsNode) :
|
getActualTypeVariable(getTypeFromTypeNode(checkNode)) === type ? getTypeFromTypeNode(extendsNode) :
|
||||||
undefined;
|
undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getConstrainedTypeVariable(typeVariable: TypeVariable, node: Node) {
|
function getConditionalFlowTypeOfType(type: Type, node: Node) {
|
||||||
let constraints: Type[] | undefined;
|
let constraints: Type[] | undefined;
|
||||||
while (node && !isStatement(node) && node.kind !== SyntaxKind.JSDocComment) {
|
while (node && !isStatement(node) && node.kind !== SyntaxKind.JSDocComment) {
|
||||||
const parent = node.parent;
|
const parent = node.parent;
|
||||||
if (parent.kind === SyntaxKind.ConditionalType && node === (<ConditionalTypeNode>parent).trueType) {
|
if (parent.kind === SyntaxKind.ConditionalType && node === (<ConditionalTypeNode>parent).trueType) {
|
||||||
const constraint = getImpliedConstraint(typeVariable, (<ConditionalTypeNode>parent).checkType, (<ConditionalTypeNode>parent).extendsType);
|
const constraint = getImpliedConstraint(type, (<ConditionalTypeNode>parent).checkType, (<ConditionalTypeNode>parent).extendsType);
|
||||||
if (constraint) {
|
if (constraint) {
|
||||||
constraints = append(constraints, constraint);
|
constraints = append(constraints, constraint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
node = parent;
|
node = parent;
|
||||||
}
|
}
|
||||||
return constraints ? getSubstitutionType(typeVariable, getIntersectionType(append(constraints, typeVariable))) : typeVariable;
|
return constraints ? getSubstitutionType(type, getIntersectionType(append(constraints, type))) : type;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isJSDocTypeReference(node: Node): node is TypeReferenceNode {
|
function isJSDocTypeReference(node: Node): node is TypeReferenceNode {
|
||||||
|
@ -12858,7 +12856,7 @@ namespace ts {
|
||||||
links.resolvedType = resolved.flags & TypeFlags.IndexedAccess &&
|
links.resolvedType = resolved.flags & TypeFlags.IndexedAccess &&
|
||||||
(<IndexedAccessType>resolved).objectType === objectType &&
|
(<IndexedAccessType>resolved).objectType === objectType &&
|
||||||
(<IndexedAccessType>resolved).indexType === indexType ?
|
(<IndexedAccessType>resolved).indexType === indexType ?
|
||||||
getConstrainedTypeVariable(<IndexedAccessType>resolved, node) : resolved;
|
getConditionalFlowTypeOfType(resolved, node) : resolved;
|
||||||
}
|
}
|
||||||
return links.resolvedType;
|
return links.resolvedType;
|
||||||
}
|
}
|
||||||
|
@ -12880,7 +12878,7 @@ namespace ts {
|
||||||
|
|
||||||
function getActualTypeVariable(type: Type): Type {
|
function getActualTypeVariable(type: Type): Type {
|
||||||
if (type.flags & TypeFlags.Substitution) {
|
if (type.flags & TypeFlags.Substitution) {
|
||||||
return (<SubstitutionType>type).typeVariable;
|
return (<SubstitutionType>type).baseType;
|
||||||
}
|
}
|
||||||
if (type.flags & TypeFlags.IndexedAccess && (
|
if (type.flags & TypeFlags.IndexedAccess && (
|
||||||
(<IndexedAccessType>type).objectType.flags & TypeFlags.Substitution ||
|
(<IndexedAccessType>type).objectType.flags & TypeFlags.Substitution ||
|
||||||
|
@ -13428,6 +13426,10 @@ namespace ts {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTypeFromTypeNode(node: TypeNode): Type {
|
function getTypeFromTypeNode(node: TypeNode): Type {
|
||||||
|
return getConditionalFlowTypeOfType(getTypeFromTypeNodeWorker(node), node);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTypeFromTypeNodeWorker(node: TypeNode): Type {
|
||||||
switch (node.kind) {
|
switch (node.kind) {
|
||||||
case SyntaxKind.AnyKeyword:
|
case SyntaxKind.AnyKeyword:
|
||||||
case SyntaxKind.JSDocAllType:
|
case SyntaxKind.JSDocAllType:
|
||||||
|
@ -13775,7 +13777,7 @@ namespace ts {
|
||||||
return !!tp.isThisType;
|
return !!tp.isThisType;
|
||||||
case SyntaxKind.Identifier:
|
case SyntaxKind.Identifier:
|
||||||
return !tp.isThisType && isPartOfTypeNode(node) && maybeTypeParameterReference(node) &&
|
return !tp.isThisType && isPartOfTypeNode(node) && maybeTypeParameterReference(node) &&
|
||||||
getTypeFromTypeNode(<TypeNode>node) === tp;
|
getTypeFromTypeNodeWorker(<TypeNode>node) === tp; // use worker because we're looking for === equality
|
||||||
case SyntaxKind.TypeQuery:
|
case SyntaxKind.TypeQuery:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -13976,7 +13978,7 @@ namespace ts {
|
||||||
return getConditionalTypeInstantiation(<ConditionalType>type, combineTypeMappers((<ConditionalType>type).mapper, mapper));
|
return getConditionalTypeInstantiation(<ConditionalType>type, combineTypeMappers((<ConditionalType>type).mapper, mapper));
|
||||||
}
|
}
|
||||||
if (flags & TypeFlags.Substitution) {
|
if (flags & TypeFlags.Substitution) {
|
||||||
const maybeVariable = instantiateType((<SubstitutionType>type).typeVariable, mapper);
|
const maybeVariable = instantiateType((<SubstitutionType>type).baseType, mapper);
|
||||||
if (maybeVariable.flags & TypeFlags.TypeVariable) {
|
if (maybeVariable.flags & TypeFlags.TypeVariable) {
|
||||||
return getSubstitutionType(maybeVariable as TypeVariable, instantiateType((<SubstitutionType>type).substitute, mapper));
|
return getSubstitutionType(maybeVariable as TypeVariable, instantiateType((<SubstitutionType>type).substitute, mapper));
|
||||||
}
|
}
|
||||||
|
@ -14985,7 +14987,7 @@ namespace ts {
|
||||||
const t = isFreshLiteralType(type) ? (<FreshableType>type).regularType :
|
const t = isFreshLiteralType(type) ? (<FreshableType>type).regularType :
|
||||||
getObjectFlags(type) & ObjectFlags.Reference && (<TypeReference>type).node ? createTypeReference((<TypeReference>type).target, getTypeArguments(<TypeReference>type)) :
|
getObjectFlags(type) & ObjectFlags.Reference && (<TypeReference>type).node ? createTypeReference((<TypeReference>type).target, getTypeArguments(<TypeReference>type)) :
|
||||||
type.flags & TypeFlags.UnionOrIntersection ? getReducedType(type) :
|
type.flags & TypeFlags.UnionOrIntersection ? getReducedType(type) :
|
||||||
type.flags & TypeFlags.Substitution ? writing ? (<SubstitutionType>type).typeVariable : (<SubstitutionType>type).substitute :
|
type.flags & TypeFlags.Substitution ? writing ? (<SubstitutionType>type).baseType : (<SubstitutionType>type).substitute :
|
||||||
type.flags & TypeFlags.Simplifiable ? getSimplifiedType(type, writing) :
|
type.flags & TypeFlags.Simplifiable ? getSimplifiedType(type, writing) :
|
||||||
type;
|
type;
|
||||||
if (t === type) break;
|
if (t === type) break;
|
||||||
|
|
|
@ -4808,8 +4808,8 @@ namespace ts {
|
||||||
// Thus, if Foo has a 'string' constraint on its type parameter, T will satisfy it. Substitution
|
// Thus, if Foo has a 'string' constraint on its type parameter, T will satisfy it. Substitution
|
||||||
// types disappear upon instantiation (just like type parameters).
|
// types disappear upon instantiation (just like type parameters).
|
||||||
export interface SubstitutionType extends InstantiableType {
|
export interface SubstitutionType extends InstantiableType {
|
||||||
typeVariable: TypeVariable; // Target type variable
|
baseType: Type; // Target type
|
||||||
substitute: Type; // Type to substitute for type parameter
|
substitute: Type; // Type to substitute for type parameter
|
||||||
}
|
}
|
||||||
|
|
||||||
/* @internal */
|
/* @internal */
|
||||||
|
|
|
@ -2523,7 +2523,7 @@ declare namespace ts {
|
||||||
resolvedFalseType: Type;
|
resolvedFalseType: Type;
|
||||||
}
|
}
|
||||||
export interface SubstitutionType extends InstantiableType {
|
export interface SubstitutionType extends InstantiableType {
|
||||||
typeVariable: TypeVariable;
|
baseType: Type;
|
||||||
substitute: Type;
|
substitute: Type;
|
||||||
}
|
}
|
||||||
export enum SignatureKind {
|
export enum SignatureKind {
|
||||||
|
|
|
@ -2523,7 +2523,7 @@ declare namespace ts {
|
||||||
resolvedFalseType: Type;
|
resolvedFalseType: Type;
|
||||||
}
|
}
|
||||||
export interface SubstitutionType extends InstantiableType {
|
export interface SubstitutionType extends InstantiableType {
|
||||||
typeVariable: TypeVariable;
|
baseType: Type;
|
||||||
substitute: Type;
|
substitute: Type;
|
||||||
}
|
}
|
||||||
export enum SignatureKind {
|
export enum SignatureKind {
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
//// [inlineConditionalHasSimilarAssignability.ts]
|
||||||
|
type MyExtract<T, U> = T extends U ? T : never
|
||||||
|
|
||||||
|
function foo<T>(a: T) {
|
||||||
|
const b: Extract<any[], T> = 0 as any;
|
||||||
|
a = b; // ok
|
||||||
|
|
||||||
|
const c: (any[] extends T ? any[] : never) = 0 as any;
|
||||||
|
a = c;
|
||||||
|
|
||||||
|
const d: MyExtract<any[], T> = 0 as any;
|
||||||
|
a = d; // ok
|
||||||
|
|
||||||
|
type CustomType = any[] extends T ? any[] : never;
|
||||||
|
const e: CustomType = 0 as any;
|
||||||
|
a = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
//// [inlineConditionalHasSimilarAssignability.js]
|
||||||
|
function foo(a) {
|
||||||
|
var b = 0;
|
||||||
|
a = b; // ok
|
||||||
|
var c = 0;
|
||||||
|
a = c;
|
||||||
|
var d = 0;
|
||||||
|
a = d; // ok
|
||||||
|
var e = 0;
|
||||||
|
a = e;
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
=== tests/cases/compiler/inlineConditionalHasSimilarAssignability.ts ===
|
||||||
|
type MyExtract<T, U> = T extends U ? T : never
|
||||||
|
>MyExtract : Symbol(MyExtract, Decl(inlineConditionalHasSimilarAssignability.ts, 0, 0))
|
||||||
|
>T : Symbol(T, Decl(inlineConditionalHasSimilarAssignability.ts, 0, 15))
|
||||||
|
>U : Symbol(U, Decl(inlineConditionalHasSimilarAssignability.ts, 0, 17))
|
||||||
|
>T : Symbol(T, Decl(inlineConditionalHasSimilarAssignability.ts, 0, 15))
|
||||||
|
>U : Symbol(U, Decl(inlineConditionalHasSimilarAssignability.ts, 0, 17))
|
||||||
|
>T : Symbol(T, Decl(inlineConditionalHasSimilarAssignability.ts, 0, 15))
|
||||||
|
|
||||||
|
function foo<T>(a: T) {
|
||||||
|
>foo : Symbol(foo, Decl(inlineConditionalHasSimilarAssignability.ts, 0, 46))
|
||||||
|
>T : Symbol(T, Decl(inlineConditionalHasSimilarAssignability.ts, 2, 13))
|
||||||
|
>a : Symbol(a, Decl(inlineConditionalHasSimilarAssignability.ts, 2, 16))
|
||||||
|
>T : Symbol(T, Decl(inlineConditionalHasSimilarAssignability.ts, 2, 13))
|
||||||
|
|
||||||
|
const b: Extract<any[], T> = 0 as any;
|
||||||
|
>b : Symbol(b, Decl(inlineConditionalHasSimilarAssignability.ts, 3, 7))
|
||||||
|
>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --))
|
||||||
|
>T : Symbol(T, Decl(inlineConditionalHasSimilarAssignability.ts, 2, 13))
|
||||||
|
|
||||||
|
a = b; // ok
|
||||||
|
>a : Symbol(a, Decl(inlineConditionalHasSimilarAssignability.ts, 2, 16))
|
||||||
|
>b : Symbol(b, Decl(inlineConditionalHasSimilarAssignability.ts, 3, 7))
|
||||||
|
|
||||||
|
const c: (any[] extends T ? any[] : never) = 0 as any;
|
||||||
|
>c : Symbol(c, Decl(inlineConditionalHasSimilarAssignability.ts, 6, 7))
|
||||||
|
>T : Symbol(T, Decl(inlineConditionalHasSimilarAssignability.ts, 2, 13))
|
||||||
|
|
||||||
|
a = c;
|
||||||
|
>a : Symbol(a, Decl(inlineConditionalHasSimilarAssignability.ts, 2, 16))
|
||||||
|
>c : Symbol(c, Decl(inlineConditionalHasSimilarAssignability.ts, 6, 7))
|
||||||
|
|
||||||
|
const d: MyExtract<any[], T> = 0 as any;
|
||||||
|
>d : Symbol(d, Decl(inlineConditionalHasSimilarAssignability.ts, 9, 7))
|
||||||
|
>MyExtract : Symbol(MyExtract, Decl(inlineConditionalHasSimilarAssignability.ts, 0, 0))
|
||||||
|
>T : Symbol(T, Decl(inlineConditionalHasSimilarAssignability.ts, 2, 13))
|
||||||
|
|
||||||
|
a = d; // ok
|
||||||
|
>a : Symbol(a, Decl(inlineConditionalHasSimilarAssignability.ts, 2, 16))
|
||||||
|
>d : Symbol(d, Decl(inlineConditionalHasSimilarAssignability.ts, 9, 7))
|
||||||
|
|
||||||
|
type CustomType = any[] extends T ? any[] : never;
|
||||||
|
>CustomType : Symbol(CustomType, Decl(inlineConditionalHasSimilarAssignability.ts, 10, 8))
|
||||||
|
>T : Symbol(T, Decl(inlineConditionalHasSimilarAssignability.ts, 2, 13))
|
||||||
|
|
||||||
|
const e: CustomType = 0 as any;
|
||||||
|
>e : Symbol(e, Decl(inlineConditionalHasSimilarAssignability.ts, 13, 7))
|
||||||
|
>CustomType : Symbol(CustomType, Decl(inlineConditionalHasSimilarAssignability.ts, 10, 8))
|
||||||
|
|
||||||
|
a = e;
|
||||||
|
>a : Symbol(a, Decl(inlineConditionalHasSimilarAssignability.ts, 2, 16))
|
||||||
|
>e : Symbol(e, Decl(inlineConditionalHasSimilarAssignability.ts, 13, 7))
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
=== tests/cases/compiler/inlineConditionalHasSimilarAssignability.ts ===
|
||||||
|
type MyExtract<T, U> = T extends U ? T : never
|
||||||
|
>MyExtract : MyExtract<T, U>
|
||||||
|
|
||||||
|
function foo<T>(a: T) {
|
||||||
|
>foo : <T>(a: T) => void
|
||||||
|
>a : T
|
||||||
|
|
||||||
|
const b: Extract<any[], T> = 0 as any;
|
||||||
|
>b : Extract<any[], T>
|
||||||
|
>0 as any : any
|
||||||
|
>0 : 0
|
||||||
|
|
||||||
|
a = b; // ok
|
||||||
|
>a = b : Extract<any[], T>
|
||||||
|
>a : T
|
||||||
|
>b : Extract<any[], T>
|
||||||
|
|
||||||
|
const c: (any[] extends T ? any[] : never) = 0 as any;
|
||||||
|
>c : any[] extends T ? any[] : never
|
||||||
|
>0 as any : any
|
||||||
|
>0 : 0
|
||||||
|
|
||||||
|
a = c;
|
||||||
|
>a = c : any[] extends T ? any[] : never
|
||||||
|
>a : T
|
||||||
|
>c : any[] extends T ? any[] : never
|
||||||
|
|
||||||
|
const d: MyExtract<any[], T> = 0 as any;
|
||||||
|
>d : MyExtract<any[], T>
|
||||||
|
>0 as any : any
|
||||||
|
>0 : 0
|
||||||
|
|
||||||
|
a = d; // ok
|
||||||
|
>a = d : MyExtract<any[], T>
|
||||||
|
>a : T
|
||||||
|
>d : MyExtract<any[], T>
|
||||||
|
|
||||||
|
type CustomType = any[] extends T ? any[] : never;
|
||||||
|
>CustomType : any[] extends T ? any[] : never
|
||||||
|
|
||||||
|
const e: CustomType = 0 as any;
|
||||||
|
>e : any[] extends T ? any[] : never
|
||||||
|
>0 as any : any
|
||||||
|
>0 : 0
|
||||||
|
|
||||||
|
a = e;
|
||||||
|
>a = e : any[] extends T ? any[] : never
|
||||||
|
>a : T
|
||||||
|
>e : any[] extends T ? any[] : never
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
type MyExtract<T, U> = T extends U ? T : never
|
||||||
|
|
||||||
|
function foo<T>(a: T) {
|
||||||
|
const b: Extract<any[], T> = 0 as any;
|
||||||
|
a = b; // ok
|
||||||
|
|
||||||
|
const c: (any[] extends T ? any[] : never) = 0 as any;
|
||||||
|
a = c;
|
||||||
|
|
||||||
|
const d: MyExtract<any[], T> = 0 as any;
|
||||||
|
a = d; // ok
|
||||||
|
|
||||||
|
type CustomType = any[] extends T ? any[] : never;
|
||||||
|
const e: CustomType = 0 as any;
|
||||||
|
a = e;
|
||||||
|
}
|
Loading…
Reference in a new issue