mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-05 08:11:30 -06:00
Instantiate earlier inferred constraints in conditional types (#57362)
This commit is contained in:
parent
91e67ffbea
commit
feb57c269b
@ -18642,11 +18642,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return type;
|
||||
}
|
||||
|
||||
function maybeCloneTypeParameter(p: TypeParameter) {
|
||||
const constraint = getConstraintOfTypeParameter(p);
|
||||
return constraint && (isGenericObjectType(constraint) || isGenericIndexType(constraint)) ? cloneTypeParameter(p) : p;
|
||||
}
|
||||
|
||||
function isSimpleTupleType(node: TypeNode): boolean {
|
||||
return isTupleTypeNode(node) && length(node.elements) > 0 &&
|
||||
!some(node.elements, e => isOptionalTypeNode(e) || isRestTypeNode(e) || isNamedTupleMember(e) && !!(e.questionToken || e.dotDotDotToken));
|
||||
@ -18689,7 +18684,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
let combinedMapper: TypeMapper | undefined;
|
||||
if (root.inferTypeParameters) {
|
||||
// When we're looking at making an inference for an infer type, when we get its constraint, it'll automagically be
|
||||
// instantiated with the context, so it doesn't need the mapper for the inference contex - however the constraint
|
||||
// instantiated with the context, so it doesn't need the mapper for the inference context - however the constraint
|
||||
// may refer to another _root_, _uncloned_ `infer` type parameter [1], or to something mapped by `mapper` [2].
|
||||
// [1] Eg, if we have `Foo<T, U extends T>` and `Foo<number, infer B>` - `B` is constrained to `T`, which, in turn, has been instantiated
|
||||
// as `number`
|
||||
@ -18697,36 +18692,25 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
// [2] Eg, if we have `Foo<T, U extends T>` and `Foo<Q, infer B>` where `Q` is mapped by `mapper` into `number` - `B` is constrained to `T`
|
||||
// which is in turn instantiated as `Q`, which is in turn instantiated as `number`.
|
||||
// So we need to:
|
||||
// * Clone the type parameters so their constraints can be instantiated in the context of `mapper` (otherwise theyd only get inference context information)
|
||||
// * Set the clones to both map the conditional's enclosing `mapper` and the original params
|
||||
// * instantiate the extends type with the clones
|
||||
// * combine `context.nonFixingMapper` with `mapper` so their constraints can be instantiated in the context of `mapper` (otherwise they'd only get inference context information)
|
||||
// * incorporate all of the component mappers into the combined mapper for the true and false members
|
||||
// This means we have three mappers that need applying:
|
||||
// This means we have two mappers that need applying:
|
||||
// * The original `mapper` used to create this conditional
|
||||
// * The mapper that maps the old root type parameter to the clone (`freshMapper`)
|
||||
// * The mapper that maps the clone to its inference result (`context.mapper`)
|
||||
const freshParams = sameMap(root.inferTypeParameters, maybeCloneTypeParameter);
|
||||
const freshMapper = freshParams !== root.inferTypeParameters ? createTypeMapper(root.inferTypeParameters, freshParams) : undefined;
|
||||
const context = createInferenceContext(freshParams, /*signature*/ undefined, InferenceFlags.None);
|
||||
if (freshMapper) {
|
||||
const freshCombinedMapper = combineTypeMappers(mapper, freshMapper);
|
||||
for (let i = 0; i < freshParams.length; i++) {
|
||||
if (freshParams[i] !== root.inferTypeParameters[i]) {
|
||||
freshParams[i].mapper = freshCombinedMapper;
|
||||
}
|
||||
}
|
||||
// * The mapper that maps the infer type parameter to its inference result (`context.mapper`)
|
||||
const context = createInferenceContext(root.inferTypeParameters, /*signature*/ undefined, InferenceFlags.None);
|
||||
if (mapper) {
|
||||
context.nonFixingMapper = combineTypeMappers(context.nonFixingMapper, mapper);
|
||||
}
|
||||
if (!checkTypeDeferred) {
|
||||
// We don't want inferences from constraints as they may cause us to eagerly resolve the
|
||||
// conditional type instead of deferring resolution. Also, we always want strict function
|
||||
// types rules (i.e. proper contravariance) for inferences.
|
||||
inferTypes(context.inferences, checkType, instantiateType(extendsType, freshMapper), InferencePriority.NoConstraints | InferencePriority.AlwaysStrict);
|
||||
inferTypes(context.inferences, checkType, extendsType, InferencePriority.NoConstraints | InferencePriority.AlwaysStrict);
|
||||
}
|
||||
const innerMapper = combineTypeMappers(freshMapper, context.mapper);
|
||||
// It's possible for 'infer T' type paramteters to be given uninstantiated constraints when the
|
||||
// those type parameters are used in type references (see getInferredTypeParameterConstraint). For
|
||||
// that reason we need context.mapper to be first in the combined mapper. See #42636 for examples.
|
||||
combinedMapper = mapper ? combineTypeMappers(innerMapper, mapper) : innerMapper;
|
||||
combinedMapper = mapper ? combineTypeMappers(context.mapper, mapper) : context.mapper;
|
||||
}
|
||||
// Instantiate the extends type including inferences for 'infer T' type parameters
|
||||
const inferredExtendsType = combinedMapper ? instantiateType(root.extendsType, combinedMapper) : extendsType;
|
||||
|
||||
@ -18,8 +18,62 @@ type Constrain<T extends C, C> = unknown;
|
||||
type Foo<A> = A extends Constrain<infer X, A> ? X : never;
|
||||
|
||||
type T0 = Foo<string>; // string
|
||||
|
||||
// https://github.com/microsoft/TypeScript/issues/57286#issuecomment-1927920336
|
||||
|
||||
class BaseClass<V> {
|
||||
protected fake(): V {
|
||||
throw new Error("");
|
||||
}
|
||||
}
|
||||
|
||||
class Klass<V> extends BaseClass<V> {
|
||||
child = true;
|
||||
}
|
||||
|
||||
type Constructor<V, P extends BaseClass<V>> = new () => P;
|
||||
type inferTest<V, T> = T extends Constructor<V, infer P> ? P : never;
|
||||
|
||||
type U = inferTest<number, Constructor<number, Klass<number>>>;
|
||||
|
||||
declare let m: U;
|
||||
m.child; // ok
|
||||
|
||||
|
||||
//// [inferTypeParameterConstraints.js]
|
||||
"use strict";
|
||||
// Repro from #42636
|
||||
var __extends = (this && this.__extends) || (function () {
|
||||
var extendStatics = function (d, b) {
|
||||
extendStatics = Object.setPrototypeOf ||
|
||||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
||||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
||||
return extendStatics(d, b);
|
||||
};
|
||||
return function (d, b) {
|
||||
if (typeof b !== "function" && b !== null)
|
||||
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
||||
extendStatics(d, b);
|
||||
function __() { this.constructor = d; }
|
||||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
||||
};
|
||||
})();
|
||||
// https://github.com/microsoft/TypeScript/issues/57286#issuecomment-1927920336
|
||||
var BaseClass = /** @class */ (function () {
|
||||
function BaseClass() {
|
||||
}
|
||||
BaseClass.prototype.fake = function () {
|
||||
throw new Error("");
|
||||
};
|
||||
return BaseClass;
|
||||
}());
|
||||
var Klass = /** @class */ (function (_super) {
|
||||
__extends(Klass, _super);
|
||||
function Klass() {
|
||||
var _this = _super !== null && _super.apply(this, arguments) || this;
|
||||
_this.child = true;
|
||||
return _this;
|
||||
}
|
||||
return Klass;
|
||||
}(BaseClass));
|
||||
m.child; // ok
|
||||
|
||||
@ -51,3 +51,61 @@ type T0 = Foo<string>; // string
|
||||
>T0 : Symbol(T0, Decl(inferTypeParameterConstraints.ts, 14, 58))
|
||||
>Foo : Symbol(Foo, Decl(inferTypeParameterConstraints.ts, 12, 41))
|
||||
|
||||
// https://github.com/microsoft/TypeScript/issues/57286#issuecomment-1927920336
|
||||
|
||||
class BaseClass<V> {
|
||||
>BaseClass : Symbol(BaseClass, Decl(inferTypeParameterConstraints.ts, 16, 22))
|
||||
>V : Symbol(V, Decl(inferTypeParameterConstraints.ts, 20, 16))
|
||||
|
||||
protected fake(): V {
|
||||
>fake : Symbol(BaseClass.fake, Decl(inferTypeParameterConstraints.ts, 20, 20))
|
||||
>V : Symbol(V, Decl(inferTypeParameterConstraints.ts, 20, 16))
|
||||
|
||||
throw new Error("");
|
||||
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
}
|
||||
}
|
||||
|
||||
class Klass<V> extends BaseClass<V> {
|
||||
>Klass : Symbol(Klass, Decl(inferTypeParameterConstraints.ts, 24, 1))
|
||||
>V : Symbol(V, Decl(inferTypeParameterConstraints.ts, 26, 12))
|
||||
>BaseClass : Symbol(BaseClass, Decl(inferTypeParameterConstraints.ts, 16, 22))
|
||||
>V : Symbol(V, Decl(inferTypeParameterConstraints.ts, 26, 12))
|
||||
|
||||
child = true;
|
||||
>child : Symbol(Klass.child, Decl(inferTypeParameterConstraints.ts, 26, 37))
|
||||
}
|
||||
|
||||
type Constructor<V, P extends BaseClass<V>> = new () => P;
|
||||
>Constructor : Symbol(Constructor, Decl(inferTypeParameterConstraints.ts, 28, 1))
|
||||
>V : Symbol(V, Decl(inferTypeParameterConstraints.ts, 30, 17))
|
||||
>P : Symbol(P, Decl(inferTypeParameterConstraints.ts, 30, 19))
|
||||
>BaseClass : Symbol(BaseClass, Decl(inferTypeParameterConstraints.ts, 16, 22))
|
||||
>V : Symbol(V, Decl(inferTypeParameterConstraints.ts, 30, 17))
|
||||
>P : Symbol(P, Decl(inferTypeParameterConstraints.ts, 30, 19))
|
||||
|
||||
type inferTest<V, T> = T extends Constructor<V, infer P> ? P : never;
|
||||
>inferTest : Symbol(inferTest, Decl(inferTypeParameterConstraints.ts, 30, 58))
|
||||
>V : Symbol(V, Decl(inferTypeParameterConstraints.ts, 31, 15))
|
||||
>T : Symbol(T, Decl(inferTypeParameterConstraints.ts, 31, 17))
|
||||
>T : Symbol(T, Decl(inferTypeParameterConstraints.ts, 31, 17))
|
||||
>Constructor : Symbol(Constructor, Decl(inferTypeParameterConstraints.ts, 28, 1))
|
||||
>V : Symbol(V, Decl(inferTypeParameterConstraints.ts, 31, 15))
|
||||
>P : Symbol(P, Decl(inferTypeParameterConstraints.ts, 31, 53))
|
||||
>P : Symbol(P, Decl(inferTypeParameterConstraints.ts, 31, 53))
|
||||
|
||||
type U = inferTest<number, Constructor<number, Klass<number>>>;
|
||||
>U : Symbol(U, Decl(inferTypeParameterConstraints.ts, 31, 69))
|
||||
>inferTest : Symbol(inferTest, Decl(inferTypeParameterConstraints.ts, 30, 58))
|
||||
>Constructor : Symbol(Constructor, Decl(inferTypeParameterConstraints.ts, 28, 1))
|
||||
>Klass : Symbol(Klass, Decl(inferTypeParameterConstraints.ts, 24, 1))
|
||||
|
||||
declare let m: U;
|
||||
>m : Symbol(m, Decl(inferTypeParameterConstraints.ts, 35, 11))
|
||||
>U : Symbol(U, Decl(inferTypeParameterConstraints.ts, 31, 69))
|
||||
|
||||
m.child; // ok
|
||||
>m.child : Symbol(Klass.child, Decl(inferTypeParameterConstraints.ts, 26, 37))
|
||||
>m : Symbol(m, Decl(inferTypeParameterConstraints.ts, 35, 11))
|
||||
>child : Symbol(Klass.child, Decl(inferTypeParameterConstraints.ts, 26, 37))
|
||||
|
||||
|
||||
@ -26,3 +26,44 @@ type Foo<A> = A extends Constrain<infer X, A> ? X : never;
|
||||
type T0 = Foo<string>; // string
|
||||
>T0 : string
|
||||
|
||||
// https://github.com/microsoft/TypeScript/issues/57286#issuecomment-1927920336
|
||||
|
||||
class BaseClass<V> {
|
||||
>BaseClass : BaseClass<V>
|
||||
|
||||
protected fake(): V {
|
||||
>fake : () => V
|
||||
|
||||
throw new Error("");
|
||||
>new Error("") : Error
|
||||
>Error : ErrorConstructor
|
||||
>"" : ""
|
||||
}
|
||||
}
|
||||
|
||||
class Klass<V> extends BaseClass<V> {
|
||||
>Klass : Klass<V>
|
||||
>BaseClass : BaseClass<V>
|
||||
|
||||
child = true;
|
||||
>child : boolean
|
||||
>true : true
|
||||
}
|
||||
|
||||
type Constructor<V, P extends BaseClass<V>> = new () => P;
|
||||
>Constructor : Constructor<V, P>
|
||||
|
||||
type inferTest<V, T> = T extends Constructor<V, infer P> ? P : never;
|
||||
>inferTest : inferTest<V, T>
|
||||
|
||||
type U = inferTest<number, Constructor<number, Klass<number>>>;
|
||||
>U : Klass<number>
|
||||
|
||||
declare let m: U;
|
||||
>m : Klass<number>
|
||||
|
||||
m.child; // ok
|
||||
>m.child : boolean
|
||||
>m : Klass<number>
|
||||
>child : boolean
|
||||
|
||||
|
||||
@ -17,3 +17,23 @@ type Constrain<T extends C, C> = unknown;
|
||||
type Foo<A> = A extends Constrain<infer X, A> ? X : never;
|
||||
|
||||
type T0 = Foo<string>; // string
|
||||
|
||||
// https://github.com/microsoft/TypeScript/issues/57286#issuecomment-1927920336
|
||||
|
||||
class BaseClass<V> {
|
||||
protected fake(): V {
|
||||
throw new Error("");
|
||||
}
|
||||
}
|
||||
|
||||
class Klass<V> extends BaseClass<V> {
|
||||
child = true;
|
||||
}
|
||||
|
||||
type Constructor<V, P extends BaseClass<V>> = new () => P;
|
||||
type inferTest<V, T> = T extends Constructor<V, infer P> ? P : never;
|
||||
|
||||
type U = inferTest<number, Constructor<number, Klass<number>>>;
|
||||
|
||||
declare let m: U;
|
||||
m.child; // ok
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user