Merge pull request #29647 from Microsoft/noConstraintsDuringInference
Only check constraints in final phase of type inference
This commit is contained in:
commit
8827bed0e7
7 changed files with 218 additions and 2 deletions
|
@ -14113,7 +14113,9 @@ namespace ts {
|
||||||
function mapper(t: Type): Type {
|
function mapper(t: Type): Type {
|
||||||
for (let i = 0; i < inferences.length; i++) {
|
for (let i = 0; i < inferences.length; i++) {
|
||||||
if (t === inferences[i].typeParameter) {
|
if (t === inferences[i].typeParameter) {
|
||||||
inferences[i].isFixed = true;
|
if (!(context.flags & InferenceFlags.NoFixing)) {
|
||||||
|
inferences[i].isFixed = true;
|
||||||
|
}
|
||||||
return getInferredType(context, i);
|
return getInferredType(context, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14839,10 +14841,12 @@ namespace ts {
|
||||||
|
|
||||||
const constraint = getConstraintOfTypeParameter(inference.typeParameter);
|
const constraint = getConstraintOfTypeParameter(inference.typeParameter);
|
||||||
if (constraint) {
|
if (constraint) {
|
||||||
|
context.flags |= InferenceFlags.NoFixing;
|
||||||
const instantiatedConstraint = instantiateType(constraint, context);
|
const instantiatedConstraint = instantiateType(constraint, context);
|
||||||
if (!context.compareTypes(inferredType, getTypeWithThisArgument(instantiatedConstraint, inferredType))) {
|
if (!context.compareTypes(inferredType, getTypeWithThisArgument(instantiatedConstraint, inferredType))) {
|
||||||
inference.inferredType = inferredType = instantiatedConstraint;
|
inference.inferredType = inferredType = instantiatedConstraint;
|
||||||
}
|
}
|
||||||
|
context.flags &= ~InferenceFlags.NoFixing;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4341,6 +4341,7 @@ namespace ts {
|
||||||
None = 0, // No special inference behaviors
|
None = 0, // No special inference behaviors
|
||||||
NoDefault = 1 << 0, // Infer unknownType for no inferences (otherwise anyType or emptyObjectType)
|
NoDefault = 1 << 0, // Infer unknownType for no inferences (otherwise anyType or emptyObjectType)
|
||||||
AnyDefault = 1 << 1, // Infer anyType for no inferences (otherwise emptyObjectType)
|
AnyDefault = 1 << 1, // Infer anyType for no inferences (otherwise emptyObjectType)
|
||||||
|
NoFixing = 1 << 2, // Disable type parameter fixing
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
//// [inferenceAndSelfReferentialConstraint.ts]
|
||||||
|
// @strict
|
||||||
|
|
||||||
|
// Repro from #29520
|
||||||
|
|
||||||
|
type Test<K extends keyof any> = {
|
||||||
|
[P in K | "foo"]: P extends "foo" ? true : () => any
|
||||||
|
}
|
||||||
|
|
||||||
|
function test<T extends Test<keyof T>>(arg: T) {
|
||||||
|
return arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
const res1 = test({
|
||||||
|
foo: true,
|
||||||
|
bar() {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const res2 = test({
|
||||||
|
foo: true,
|
||||||
|
bar: function () {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const res3 = test({
|
||||||
|
foo: true,
|
||||||
|
bar: () => {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
//// [inferenceAndSelfReferentialConstraint.js]
|
||||||
|
// @strict
|
||||||
|
function test(arg) {
|
||||||
|
return arg;
|
||||||
|
}
|
||||||
|
var res1 = test({
|
||||||
|
foo: true,
|
||||||
|
bar: function () {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var res2 = test({
|
||||||
|
foo: true,
|
||||||
|
bar: function () {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var res3 = test({
|
||||||
|
foo: true,
|
||||||
|
bar: function () {
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,63 @@
|
||||||
|
=== tests/cases/compiler/inferenceAndSelfReferentialConstraint.ts ===
|
||||||
|
// @strict
|
||||||
|
|
||||||
|
// Repro from #29520
|
||||||
|
|
||||||
|
type Test<K extends keyof any> = {
|
||||||
|
>Test : Symbol(Test, Decl(inferenceAndSelfReferentialConstraint.ts, 0, 0))
|
||||||
|
>K : Symbol(K, Decl(inferenceAndSelfReferentialConstraint.ts, 4, 10))
|
||||||
|
|
||||||
|
[P in K | "foo"]: P extends "foo" ? true : () => any
|
||||||
|
>P : Symbol(P, Decl(inferenceAndSelfReferentialConstraint.ts, 5, 3))
|
||||||
|
>K : Symbol(K, Decl(inferenceAndSelfReferentialConstraint.ts, 4, 10))
|
||||||
|
>P : Symbol(P, Decl(inferenceAndSelfReferentialConstraint.ts, 5, 3))
|
||||||
|
}
|
||||||
|
|
||||||
|
function test<T extends Test<keyof T>>(arg: T) {
|
||||||
|
>test : Symbol(test, Decl(inferenceAndSelfReferentialConstraint.ts, 6, 1))
|
||||||
|
>T : Symbol(T, Decl(inferenceAndSelfReferentialConstraint.ts, 8, 14))
|
||||||
|
>Test : Symbol(Test, Decl(inferenceAndSelfReferentialConstraint.ts, 0, 0))
|
||||||
|
>T : Symbol(T, Decl(inferenceAndSelfReferentialConstraint.ts, 8, 14))
|
||||||
|
>arg : Symbol(arg, Decl(inferenceAndSelfReferentialConstraint.ts, 8, 39))
|
||||||
|
>T : Symbol(T, Decl(inferenceAndSelfReferentialConstraint.ts, 8, 14))
|
||||||
|
|
||||||
|
return arg;
|
||||||
|
>arg : Symbol(arg, Decl(inferenceAndSelfReferentialConstraint.ts, 8, 39))
|
||||||
|
}
|
||||||
|
|
||||||
|
const res1 = test({
|
||||||
|
>res1 : Symbol(res1, Decl(inferenceAndSelfReferentialConstraint.ts, 12, 5))
|
||||||
|
>test : Symbol(test, Decl(inferenceAndSelfReferentialConstraint.ts, 6, 1))
|
||||||
|
|
||||||
|
foo: true,
|
||||||
|
>foo : Symbol(foo, Decl(inferenceAndSelfReferentialConstraint.ts, 12, 19))
|
||||||
|
|
||||||
|
bar() {
|
||||||
|
>bar : Symbol(bar, Decl(inferenceAndSelfReferentialConstraint.ts, 13, 12))
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const res2 = test({
|
||||||
|
>res2 : Symbol(res2, Decl(inferenceAndSelfReferentialConstraint.ts, 18, 5))
|
||||||
|
>test : Symbol(test, Decl(inferenceAndSelfReferentialConstraint.ts, 6, 1))
|
||||||
|
|
||||||
|
foo: true,
|
||||||
|
>foo : Symbol(foo, Decl(inferenceAndSelfReferentialConstraint.ts, 18, 19))
|
||||||
|
|
||||||
|
bar: function () {
|
||||||
|
>bar : Symbol(bar, Decl(inferenceAndSelfReferentialConstraint.ts, 19, 12))
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const res3 = test({
|
||||||
|
>res3 : Symbol(res3, Decl(inferenceAndSelfReferentialConstraint.ts, 24, 5))
|
||||||
|
>test : Symbol(test, Decl(inferenceAndSelfReferentialConstraint.ts, 6, 1))
|
||||||
|
|
||||||
|
foo: true,
|
||||||
|
>foo : Symbol(foo, Decl(inferenceAndSelfReferentialConstraint.ts, 24, 19))
|
||||||
|
|
||||||
|
bar: () => {
|
||||||
|
>bar : Symbol(bar, Decl(inferenceAndSelfReferentialConstraint.ts, 25, 12))
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
=== tests/cases/compiler/inferenceAndSelfReferentialConstraint.ts ===
|
||||||
|
// @strict
|
||||||
|
|
||||||
|
// Repro from #29520
|
||||||
|
|
||||||
|
type Test<K extends keyof any> = {
|
||||||
|
>Test : Test<K>
|
||||||
|
|
||||||
|
[P in K | "foo"]: P extends "foo" ? true : () => any
|
||||||
|
>true : true
|
||||||
|
}
|
||||||
|
|
||||||
|
function test<T extends Test<keyof T>>(arg: T) {
|
||||||
|
>test : <T extends Test<keyof T>>(arg: T) => T
|
||||||
|
>arg : T
|
||||||
|
|
||||||
|
return arg;
|
||||||
|
>arg : T
|
||||||
|
}
|
||||||
|
|
||||||
|
const res1 = test({
|
||||||
|
>res1 : { foo: true; bar(): void; }
|
||||||
|
>test({ foo: true, bar() { }}) : { foo: true; bar(): void; }
|
||||||
|
>test : <T extends Test<keyof T>>(arg: T) => T
|
||||||
|
>{ foo: true, bar() { }} : { foo: true; bar(): void; }
|
||||||
|
|
||||||
|
foo: true,
|
||||||
|
>foo : true
|
||||||
|
>true : true
|
||||||
|
|
||||||
|
bar() {
|
||||||
|
>bar : () => void
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const res2 = test({
|
||||||
|
>res2 : { foo: true; bar: () => void; }
|
||||||
|
>test({ foo: true, bar: function () { }}) : { foo: true; bar: () => void; }
|
||||||
|
>test : <T extends Test<keyof T>>(arg: T) => T
|
||||||
|
>{ foo: true, bar: function () { }} : { foo: true; bar: () => void; }
|
||||||
|
|
||||||
|
foo: true,
|
||||||
|
>foo : true
|
||||||
|
>true : true
|
||||||
|
|
||||||
|
bar: function () {
|
||||||
|
>bar : () => void
|
||||||
|
>function () { } : () => void
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const res3 = test({
|
||||||
|
>res3 : { foo: true; bar: () => void; }
|
||||||
|
>test({ foo: true, bar: () => { }}) : { foo: true; bar: () => void; }
|
||||||
|
>test : <T extends Test<keyof T>>(arg: T) => T
|
||||||
|
>{ foo: true, bar: () => { }} : { foo: true; bar: () => void; }
|
||||||
|
|
||||||
|
foo: true,
|
||||||
|
>foo : true
|
||||||
|
>true : true
|
||||||
|
|
||||||
|
bar: () => {
|
||||||
|
>bar : () => void
|
||||||
|
>() => { } : () => void
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
|
@ -52,7 +52,7 @@ function f(t, u, v, w, x) {
|
||||||
}
|
}
|
||||||
|
|
||||||
f({ a: 12, b: 'hi', c: null }, undefined, { c: false, d: 12, b: undefined }, 101, 'nope');
|
f({ a: 12, b: 'hi', c: null }, undefined, { c: false, d: 12, b: undefined }, 101, 'nope');
|
||||||
>f({ a: 12, b: 'hi', c: null }, undefined, { c: false, d: 12, b: undefined }, 101, 'nope') : string | number
|
>f({ a: 12, b: 'hi', c: null }, undefined, { c: false, d: 12, b: undefined }, 101, 'nope') : 101 | "nope"
|
||||||
>f : <T extends { a: number; b: string; }, U, V extends { c: boolean; }, W, X>(t: T, u: U, v: V, w: W, x: X) => W | X
|
>f : <T extends { a: number; b: string; }, U, V extends { c: boolean; }, W, X>(t: T, u: U, v: V, w: W, x: X) => W | X
|
||||||
>{ a: 12, b: 'hi', c: null } : { a: number; b: string; c: null; }
|
>{ a: 12, b: 'hi', c: null } : { a: number; b: string; c: null; }
|
||||||
>a : number
|
>a : number
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
// @strict
|
||||||
|
|
||||||
|
// Repro from #29520
|
||||||
|
|
||||||
|
type Test<K extends keyof any> = {
|
||||||
|
[P in K | "foo"]: P extends "foo" ? true : () => any
|
||||||
|
}
|
||||||
|
|
||||||
|
function test<T extends Test<keyof T>>(arg: T) {
|
||||||
|
return arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
const res1 = test({
|
||||||
|
foo: true,
|
||||||
|
bar() {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const res2 = test({
|
||||||
|
foo: true,
|
||||||
|
bar: function () {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const res3 = test({
|
||||||
|
foo: true,
|
||||||
|
bar: () => {
|
||||||
|
}
|
||||||
|
});
|
Loading…
Reference in a new issue