Combine keyof T inferences (#22525)

* Combine keyof T inferences

* Extract covariant inference derivation into function

* Test:keyof inference lower priority than return inference

for #22376

* Update 'expected' comment in keyofInferenceLowerPriorityThanReturn

* Update comment in test too, not just baselines

* Fix typo

* Move tests
This commit is contained in:
Wesley Wigham 2018-03-19 16:56:51 -07:00 committed by Daniel Rosenwasser
parent a11e54856a
commit 044cdbc85f
12 changed files with 552 additions and 28 deletions

View File

@ -11737,7 +11737,10 @@ namespace ts {
else if ((isLiteralType(source) || source.flags & TypeFlags.String) && target.flags & TypeFlags.Index) {
const empty = createEmptyObjectTypeFromStringLiteral(source);
contravariant = !contravariant;
const savePriority = priority;
priority |= InferencePriority.LiteralKeyof;
inferFromTypes(empty, (target as IndexType).type);
priority = savePriority;
contravariant = !contravariant;
}
else if (source.flags & TypeFlags.IndexedAccess && target.flags & TypeFlags.IndexedAccess) {
@ -11980,6 +11983,30 @@ namespace ts {
return candidates;
}
function getContravariantInference(inference: InferenceInfo) {
return inference.priority & InferencePriority.PriorityImpliesCombination ? getIntersectionType(inference.contraCandidates) : getCommonSubtype(inference.contraCandidates);
}
function getCovariantInference(inference: InferenceInfo, context: InferenceContext, signature: Signature) {
// Extract all object literal types and replace them with a single widened and normalized type.
const candidates = widenObjectLiteralCandidates(inference.candidates);
// We widen inferred literal types if
// all inferences were made to top-level occurrences of the type parameter, and
// the type parameter has no constraint or its constraint includes no primitive or literal types, and
// the type parameter was fixed during inference or does not occur at top-level in the return type.
const widenLiteralTypes = inference.topLevel &&
!hasPrimitiveConstraint(inference.typeParameter) &&
(inference.isFixed || !isTypeParameterAtTopLevel(getReturnTypeOfSignature(signature), inference.typeParameter));
const baseCandidates = widenLiteralTypes ? sameMap(candidates, getWidenedLiteralType) : candidates;
// If all inferences were made from contravariant positions, infer a common subtype. Otherwise, if
// 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 = context.flags & InferenceFlags.InferUnionTypes || inference.priority & InferencePriority.PriorityImpliesCombination ?
getUnionType(baseCandidates, UnionReduction.Subtype) :
getCommonSupertype(baseCandidates);
return getWidenedType(unwidenedType);
}
function getInferredType(context: InferenceContext, index: number): Type {
const inference = context.inferences[index];
let inferredType = inference.inferredType;
@ -11987,32 +12014,16 @@ namespace ts {
const signature = context.signature;
if (signature) {
if (inference.candidates) {
// Extract all object literal types and replace them with a single widened and normalized type.
const candidates = widenObjectLiteralCandidates(inference.candidates);
// We widen inferred literal types if
// all inferences were made to top-level ocurrences of the type parameter, and
// the type parameter has no constraint or its constraint includes no primitive or literal types, and
// the type parameter was fixed during inference or does not occur at top-level in the return type.
const widenLiteralTypes = inference.topLevel &&
!hasPrimitiveConstraint(inference.typeParameter) &&
(inference.isFixed || !isTypeParameterAtTopLevel(getReturnTypeOfSignature(signature), inference.typeParameter));
const baseCandidates = widenLiteralTypes ? sameMap(candidates, getWidenedLiteralType) : candidates;
// If all inferences were made from contravariant positions, infer a common subtype. Otherwise, if
// 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 = context.flags & InferenceFlags.InferUnionTypes || inference.priority & InferencePriority.PriorityImpliesUnion ?
getUnionType(baseCandidates, UnionReduction.Subtype) :
getCommonSupertype(baseCandidates);
inferredType = getWidenedType(unwidenedType);
inferredType = getCovariantInference(inference, context, signature);
// If we have inferred 'never' but have contravariant candidates. To get a more specific type we
// infer from the contravariant candidates instead.
if (inferredType.flags & TypeFlags.Never && inference.contraCandidates) {
inferredType = getCommonSubtype(inference.contraCandidates);
inferredType = getContravariantInference(inference);
}
}
else if (inference.contraCandidates) {
// We only have contravariant inferences, infer the best common subtype of those
inferredType = getCommonSubtype(inference.contraCandidates);
inferredType = getContravariantInference(inference);
}
else if (context.flags & InferenceFlags.NoDefault) {
// We use silentNeverType as the wildcard that signals no inferences.

View File

@ -3920,10 +3920,11 @@ namespace ts {
HomomorphicMappedType = 1 << 1, // Reverse inference for homomorphic mapped type
MappedTypeConstraint = 1 << 2, // Reverse inference for mapped type
ReturnType = 1 << 3, // Inference made from return type of generic function
NoConstraints = 1 << 4, // Don't infer from constraints of instantiable types
AlwaysStrict = 1 << 5, // Always use strict rules for contravariant inferences
LiteralKeyof = 1 << 4, // Inference made from a string literal to a keyof T
NoConstraints = 1 << 5, // Don't infer from constraints of instantiable types
AlwaysStrict = 1 << 6, // Always use strict rules for contravariant inferences
PriorityImpliesUnion = ReturnType | MappedTypeConstraint, // These priorities imply that the resulting type should be a union of all candidates
PriorityImpliesCombination = ReturnType | MappedTypeConstraint | LiteralKeyof, // These priorities imply that the resulting type should be a combination of all candidates
}
/* @internal */

View File

@ -2240,9 +2240,10 @@ declare namespace ts {
HomomorphicMappedType = 2,
MappedTypeConstraint = 4,
ReturnType = 8,
NoConstraints = 16,
AlwaysStrict = 32,
PriorityImpliesUnion = 12,
LiteralKeyof = 16,
NoConstraints = 32,
AlwaysStrict = 64,
PriorityImpliesCombination = 28,
}
interface JsFileExtensionInfo {
extension: string;

View File

@ -2240,9 +2240,10 @@ declare namespace ts {
HomomorphicMappedType = 2,
MappedTypeConstraint = 4,
ReturnType = 8,
NoConstraints = 16,
AlwaysStrict = 32,
PriorityImpliesUnion = 12,
LiteralKeyof = 16,
NoConstraints = 32,
AlwaysStrict = 64,
PriorityImpliesCombination = 28,
}
interface JsFileExtensionInfo {
extension: string;

View File

@ -0,0 +1,17 @@
//// [keyofInferenceIntersectsResults.ts]
interface X {
a: string;
b: string;
}
declare function foo<T = X>(x: keyof T, y: keyof T): T;
declare function bar<T>(x: keyof T, y: keyof T): T;
const a = foo<X>('a', 'b'); // compiles cleanly
const b = foo('a', 'b'); // also clean
const c = bar('a', 'b'); // still clean
//// [keyofInferenceIntersectsResults.js]
var a = foo('a', 'b'); // compiles cleanly
var b = foo('a', 'b'); // also clean
var c = bar('a', 'b'); // still clean

View File

@ -0,0 +1,43 @@
=== tests/cases/conformance/types/typeRelationships/typeInference/keyofInferenceIntersectsResults.ts ===
interface X {
>X : Symbol(X, Decl(keyofInferenceIntersectsResults.ts, 0, 0))
a: string;
>a : Symbol(X.a, Decl(keyofInferenceIntersectsResults.ts, 0, 13))
b: string;
>b : Symbol(X.b, Decl(keyofInferenceIntersectsResults.ts, 1, 14))
}
declare function foo<T = X>(x: keyof T, y: keyof T): T;
>foo : Symbol(foo, Decl(keyofInferenceIntersectsResults.ts, 3, 1))
>T : Symbol(T, Decl(keyofInferenceIntersectsResults.ts, 5, 21))
>X : Symbol(X, Decl(keyofInferenceIntersectsResults.ts, 0, 0))
>x : Symbol(x, Decl(keyofInferenceIntersectsResults.ts, 5, 28))
>T : Symbol(T, Decl(keyofInferenceIntersectsResults.ts, 5, 21))
>y : Symbol(y, Decl(keyofInferenceIntersectsResults.ts, 5, 39))
>T : Symbol(T, Decl(keyofInferenceIntersectsResults.ts, 5, 21))
>T : Symbol(T, Decl(keyofInferenceIntersectsResults.ts, 5, 21))
declare function bar<T>(x: keyof T, y: keyof T): T;
>bar : Symbol(bar, Decl(keyofInferenceIntersectsResults.ts, 5, 55))
>T : Symbol(T, Decl(keyofInferenceIntersectsResults.ts, 6, 21))
>x : Symbol(x, Decl(keyofInferenceIntersectsResults.ts, 6, 24))
>T : Symbol(T, Decl(keyofInferenceIntersectsResults.ts, 6, 21))
>y : Symbol(y, Decl(keyofInferenceIntersectsResults.ts, 6, 35))
>T : Symbol(T, Decl(keyofInferenceIntersectsResults.ts, 6, 21))
>T : Symbol(T, Decl(keyofInferenceIntersectsResults.ts, 6, 21))
const a = foo<X>('a', 'b'); // compiles cleanly
>a : Symbol(a, Decl(keyofInferenceIntersectsResults.ts, 8, 5))
>foo : Symbol(foo, Decl(keyofInferenceIntersectsResults.ts, 3, 1))
>X : Symbol(X, Decl(keyofInferenceIntersectsResults.ts, 0, 0))
const b = foo('a', 'b'); // also clean
>b : Symbol(b, Decl(keyofInferenceIntersectsResults.ts, 9, 5))
>foo : Symbol(foo, Decl(keyofInferenceIntersectsResults.ts, 3, 1))
const c = bar('a', 'b'); // still clean
>c : Symbol(c, Decl(keyofInferenceIntersectsResults.ts, 10, 5))
>bar : Symbol(bar, Decl(keyofInferenceIntersectsResults.ts, 5, 55))

View File

@ -0,0 +1,52 @@
=== tests/cases/conformance/types/typeRelationships/typeInference/keyofInferenceIntersectsResults.ts ===
interface X {
>X : X
a: string;
>a : string
b: string;
>b : string
}
declare function foo<T = X>(x: keyof T, y: keyof T): T;
>foo : <T = X>(x: keyof T, y: keyof T) => T
>T : T
>X : X
>x : keyof T
>T : T
>y : keyof T
>T : T
>T : T
declare function bar<T>(x: keyof T, y: keyof T): T;
>bar : <T>(x: keyof T, y: keyof T) => T
>T : T
>x : keyof T
>T : T
>y : keyof T
>T : T
>T : T
const a = foo<X>('a', 'b'); // compiles cleanly
>a : X
>foo<X>('a', 'b') : X
>foo : <T = X>(x: keyof T, y: keyof T) => T
>X : X
>'a' : "a"
>'b' : "b"
const b = foo('a', 'b'); // also clean
>b : { a: any; } & { b: any; }
>foo('a', 'b') : { a: any; } & { b: any; }
>foo : <T = X>(x: keyof T, y: keyof T) => T
>'a' : "a"
>'b' : "b"
const c = bar('a', 'b'); // still clean
>c : { a: any; } & { b: any; }
>bar('a', 'b') : { a: any; } & { b: any; }
>bar : <T>(x: keyof T, y: keyof T) => T
>'a' : "a"
>'b' : "b"

View File

@ -0,0 +1,57 @@
//// [keyofInferenceLowerPriorityThanReturn.ts]
// #22736
declare class Write {
protected dummy: Write;
}
declare class Col<s, a> {
protected dummy: [Col<s, a>, s, a];
}
declare class Table<Req, Def> {
protected dummy: [Table<Req, Def>, Req, Def];
}
type MakeTable<T1 extends object, T2 extends object> = {
[P in keyof T1]: Col<Write, T1[P]>;
} & {
[P in keyof T2]: Col<Write, T2[P]>;
};
declare class ConflictTarget<Cols> {
public static tableColumns<Cols>(cols: (keyof Cols)[]): ConflictTarget<Cols>;
protected dummy: [ConflictTarget<Cols>, Cols];
}
const bookTable: Table<BookReq, BookDef> = null as any
interface BookReq {
readonly title: string;
readonly serial: number;
}
interface BookDef {
readonly author: string;
readonly numPages: number | null;
}
function insertOnConflictDoNothing<Req extends object, Def extends object>(_table: Table<Req, Def>, _conflictTarget: ConflictTarget<Req & Def>): boolean {
throw new Error();
}
function f() {
insertOnConflictDoNothing(bookTable, ConflictTarget.tableColumns(["serial"])); // <-- No error here; should use the type inferred for the return type of `tableColumns`
}
//// [keyofInferenceLowerPriorityThanReturn.js]
var bookTable = null;
function insertOnConflictDoNothing(_table, _conflictTarget) {
throw new Error();
}
function f() {
insertOnConflictDoNothing(bookTable, ConflictTarget.tableColumns(["serial"])); // <-- No error here; should use the type inferred for the return type of `tableColumns`
}

View File

@ -0,0 +1,138 @@
=== tests/cases/conformance/types/typeRelationships/typeInference/keyofInferenceLowerPriorityThanReturn.ts ===
// #22736
declare class Write {
>Write : Symbol(Write, Decl(keyofInferenceLowerPriorityThanReturn.ts, 0, 0))
protected dummy: Write;
>dummy : Symbol(Write.dummy, Decl(keyofInferenceLowerPriorityThanReturn.ts, 1, 21))
>Write : Symbol(Write, Decl(keyofInferenceLowerPriorityThanReturn.ts, 0, 0))
}
declare class Col<s, a> {
>Col : Symbol(Col, Decl(keyofInferenceLowerPriorityThanReturn.ts, 3, 1))
>s : Symbol(s, Decl(keyofInferenceLowerPriorityThanReturn.ts, 5, 18))
>a : Symbol(a, Decl(keyofInferenceLowerPriorityThanReturn.ts, 5, 20))
protected dummy: [Col<s, a>, s, a];
>dummy : Symbol(Col.dummy, Decl(keyofInferenceLowerPriorityThanReturn.ts, 5, 25))
>Col : Symbol(Col, Decl(keyofInferenceLowerPriorityThanReturn.ts, 3, 1))
>s : Symbol(s, Decl(keyofInferenceLowerPriorityThanReturn.ts, 5, 18))
>a : Symbol(a, Decl(keyofInferenceLowerPriorityThanReturn.ts, 5, 20))
>s : Symbol(s, Decl(keyofInferenceLowerPriorityThanReturn.ts, 5, 18))
>a : Symbol(a, Decl(keyofInferenceLowerPriorityThanReturn.ts, 5, 20))
}
declare class Table<Req, Def> {
>Table : Symbol(Table, Decl(keyofInferenceLowerPriorityThanReturn.ts, 7, 1))
>Req : Symbol(Req, Decl(keyofInferenceLowerPriorityThanReturn.ts, 9, 20))
>Def : Symbol(Def, Decl(keyofInferenceLowerPriorityThanReturn.ts, 9, 24))
protected dummy: [Table<Req, Def>, Req, Def];
>dummy : Symbol(Table.dummy, Decl(keyofInferenceLowerPriorityThanReturn.ts, 9, 31))
>Table : Symbol(Table, Decl(keyofInferenceLowerPriorityThanReturn.ts, 7, 1))
>Req : Symbol(Req, Decl(keyofInferenceLowerPriorityThanReturn.ts, 9, 20))
>Def : Symbol(Def, Decl(keyofInferenceLowerPriorityThanReturn.ts, 9, 24))
>Req : Symbol(Req, Decl(keyofInferenceLowerPriorityThanReturn.ts, 9, 20))
>Def : Symbol(Def, Decl(keyofInferenceLowerPriorityThanReturn.ts, 9, 24))
}
type MakeTable<T1 extends object, T2 extends object> = {
>MakeTable : Symbol(MakeTable, Decl(keyofInferenceLowerPriorityThanReturn.ts, 11, 1))
>T1 : Symbol(T1, Decl(keyofInferenceLowerPriorityThanReturn.ts, 13, 15))
>T2 : Symbol(T2, Decl(keyofInferenceLowerPriorityThanReturn.ts, 13, 33))
[P in keyof T1]: Col<Write, T1[P]>;
>P : Symbol(P, Decl(keyofInferenceLowerPriorityThanReturn.ts, 14, 5))
>T1 : Symbol(T1, Decl(keyofInferenceLowerPriorityThanReturn.ts, 13, 15))
>Col : Symbol(Col, Decl(keyofInferenceLowerPriorityThanReturn.ts, 3, 1))
>Write : Symbol(Write, Decl(keyofInferenceLowerPriorityThanReturn.ts, 0, 0))
>T1 : Symbol(T1, Decl(keyofInferenceLowerPriorityThanReturn.ts, 13, 15))
>P : Symbol(P, Decl(keyofInferenceLowerPriorityThanReturn.ts, 14, 5))
} & {
[P in keyof T2]: Col<Write, T2[P]>;
>P : Symbol(P, Decl(keyofInferenceLowerPriorityThanReturn.ts, 16, 9))
>T2 : Symbol(T2, Decl(keyofInferenceLowerPriorityThanReturn.ts, 13, 33))
>Col : Symbol(Col, Decl(keyofInferenceLowerPriorityThanReturn.ts, 3, 1))
>Write : Symbol(Write, Decl(keyofInferenceLowerPriorityThanReturn.ts, 0, 0))
>T2 : Symbol(T2, Decl(keyofInferenceLowerPriorityThanReturn.ts, 13, 33))
>P : Symbol(P, Decl(keyofInferenceLowerPriorityThanReturn.ts, 16, 9))
};
declare class ConflictTarget<Cols> {
>ConflictTarget : Symbol(ConflictTarget, Decl(keyofInferenceLowerPriorityThanReturn.ts, 17, 6))
>Cols : Symbol(Cols, Decl(keyofInferenceLowerPriorityThanReturn.ts, 19, 29))
public static tableColumns<Cols>(cols: (keyof Cols)[]): ConflictTarget<Cols>;
>tableColumns : Symbol(ConflictTarget.tableColumns, Decl(keyofInferenceLowerPriorityThanReturn.ts, 19, 36))
>Cols : Symbol(Cols, Decl(keyofInferenceLowerPriorityThanReturn.ts, 20, 31))
>cols : Symbol(cols, Decl(keyofInferenceLowerPriorityThanReturn.ts, 20, 37))
>Cols : Symbol(Cols, Decl(keyofInferenceLowerPriorityThanReturn.ts, 20, 31))
>ConflictTarget : Symbol(ConflictTarget, Decl(keyofInferenceLowerPriorityThanReturn.ts, 17, 6))
>Cols : Symbol(Cols, Decl(keyofInferenceLowerPriorityThanReturn.ts, 20, 31))
protected dummy: [ConflictTarget<Cols>, Cols];
>dummy : Symbol(ConflictTarget.dummy, Decl(keyofInferenceLowerPriorityThanReturn.ts, 20, 81))
>ConflictTarget : Symbol(ConflictTarget, Decl(keyofInferenceLowerPriorityThanReturn.ts, 17, 6))
>Cols : Symbol(Cols, Decl(keyofInferenceLowerPriorityThanReturn.ts, 19, 29))
>Cols : Symbol(Cols, Decl(keyofInferenceLowerPriorityThanReturn.ts, 19, 29))
}
const bookTable: Table<BookReq, BookDef> = null as any
>bookTable : Symbol(bookTable, Decl(keyofInferenceLowerPriorityThanReturn.ts, 26, 5))
>Table : Symbol(Table, Decl(keyofInferenceLowerPriorityThanReturn.ts, 7, 1))
>BookReq : Symbol(BookReq, Decl(keyofInferenceLowerPriorityThanReturn.ts, 26, 54))
>BookDef : Symbol(BookDef, Decl(keyofInferenceLowerPriorityThanReturn.ts, 31, 1))
interface BookReq {
>BookReq : Symbol(BookReq, Decl(keyofInferenceLowerPriorityThanReturn.ts, 26, 54))
readonly title: string;
>title : Symbol(BookReq.title, Decl(keyofInferenceLowerPriorityThanReturn.ts, 28, 19))
readonly serial: number;
>serial : Symbol(BookReq.serial, Decl(keyofInferenceLowerPriorityThanReturn.ts, 29, 27))
}
interface BookDef {
>BookDef : Symbol(BookDef, Decl(keyofInferenceLowerPriorityThanReturn.ts, 31, 1))
readonly author: string;
>author : Symbol(BookDef.author, Decl(keyofInferenceLowerPriorityThanReturn.ts, 33, 19))
readonly numPages: number | null;
>numPages : Symbol(BookDef.numPages, Decl(keyofInferenceLowerPriorityThanReturn.ts, 34, 28))
}
function insertOnConflictDoNothing<Req extends object, Def extends object>(_table: Table<Req, Def>, _conflictTarget: ConflictTarget<Req & Def>): boolean {
>insertOnConflictDoNothing : Symbol(insertOnConflictDoNothing, Decl(keyofInferenceLowerPriorityThanReturn.ts, 36, 1))
>Req : Symbol(Req, Decl(keyofInferenceLowerPriorityThanReturn.ts, 39, 35))
>Def : Symbol(Def, Decl(keyofInferenceLowerPriorityThanReturn.ts, 39, 54))
>_table : Symbol(_table, Decl(keyofInferenceLowerPriorityThanReturn.ts, 39, 75))
>Table : Symbol(Table, Decl(keyofInferenceLowerPriorityThanReturn.ts, 7, 1))
>Req : Symbol(Req, Decl(keyofInferenceLowerPriorityThanReturn.ts, 39, 35))
>Def : Symbol(Def, Decl(keyofInferenceLowerPriorityThanReturn.ts, 39, 54))
>_conflictTarget : Symbol(_conflictTarget, Decl(keyofInferenceLowerPriorityThanReturn.ts, 39, 99))
>ConflictTarget : Symbol(ConflictTarget, Decl(keyofInferenceLowerPriorityThanReturn.ts, 17, 6))
>Req : Symbol(Req, Decl(keyofInferenceLowerPriorityThanReturn.ts, 39, 35))
>Def : Symbol(Def, Decl(keyofInferenceLowerPriorityThanReturn.ts, 39, 54))
throw new Error();
>Error : Symbol(Error, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
}
function f() {
>f : Symbol(f, Decl(keyofInferenceLowerPriorityThanReturn.ts, 41, 1))
insertOnConflictDoNothing(bookTable, ConflictTarget.tableColumns(["serial"])); // <-- No error here; should use the type inferred for the return type of `tableColumns`
>insertOnConflictDoNothing : Symbol(insertOnConflictDoNothing, Decl(keyofInferenceLowerPriorityThanReturn.ts, 36, 1))
>bookTable : Symbol(bookTable, Decl(keyofInferenceLowerPriorityThanReturn.ts, 26, 5))
>ConflictTarget.tableColumns : Symbol(ConflictTarget.tableColumns, Decl(keyofInferenceLowerPriorityThanReturn.ts, 19, 36))
>ConflictTarget : Symbol(ConflictTarget, Decl(keyofInferenceLowerPriorityThanReturn.ts, 17, 6))
>tableColumns : Symbol(ConflictTarget.tableColumns, Decl(keyofInferenceLowerPriorityThanReturn.ts, 19, 36))
}

View File

@ -0,0 +1,146 @@
=== tests/cases/conformance/types/typeRelationships/typeInference/keyofInferenceLowerPriorityThanReturn.ts ===
// #22736
declare class Write {
>Write : Write
protected dummy: Write;
>dummy : Write
>Write : Write
}
declare class Col<s, a> {
>Col : Col<s, a>
>s : s
>a : a
protected dummy: [Col<s, a>, s, a];
>dummy : [Col<s, a>, s, a]
>Col : Col<s, a>
>s : s
>a : a
>s : s
>a : a
}
declare class Table<Req, Def> {
>Table : Table<Req, Def>
>Req : Req
>Def : Def
protected dummy: [Table<Req, Def>, Req, Def];
>dummy : [Table<Req, Def>, Req, Def]
>Table : Table<Req, Def>
>Req : Req
>Def : Def
>Req : Req
>Def : Def
}
type MakeTable<T1 extends object, T2 extends object> = {
>MakeTable : MakeTable<T1, T2>
>T1 : T1
>T2 : T2
[P in keyof T1]: Col<Write, T1[P]>;
>P : P
>T1 : T1
>Col : Col<s, a>
>Write : Write
>T1 : T1
>P : P
} & {
[P in keyof T2]: Col<Write, T2[P]>;
>P : P
>T2 : T2
>Col : Col<s, a>
>Write : Write
>T2 : T2
>P : P
};
declare class ConflictTarget<Cols> {
>ConflictTarget : ConflictTarget<Cols>
>Cols : Cols
public static tableColumns<Cols>(cols: (keyof Cols)[]): ConflictTarget<Cols>;
>tableColumns : <Cols>(cols: (keyof Cols)[]) => ConflictTarget<Cols>
>Cols : Cols
>cols : (keyof Cols)[]
>Cols : Cols
>ConflictTarget : ConflictTarget<Cols>
>Cols : Cols
protected dummy: [ConflictTarget<Cols>, Cols];
>dummy : [ConflictTarget<Cols>, Cols]
>ConflictTarget : ConflictTarget<Cols>
>Cols : Cols
>Cols : Cols
}
const bookTable: Table<BookReq, BookDef> = null as any
>bookTable : Table<BookReq, BookDef>
>Table : Table<Req, Def>
>BookReq : BookReq
>BookDef : BookDef
>null as any : any
>null : null
interface BookReq {
>BookReq : BookReq
readonly title: string;
>title : string
readonly serial: number;
>serial : number
}
interface BookDef {
>BookDef : BookDef
readonly author: string;
>author : string
readonly numPages: number | null;
>numPages : number
>null : null
}
function insertOnConflictDoNothing<Req extends object, Def extends object>(_table: Table<Req, Def>, _conflictTarget: ConflictTarget<Req & Def>): boolean {
>insertOnConflictDoNothing : <Req extends object, Def extends object>(_table: Table<Req, Def>, _conflictTarget: ConflictTarget<Req & Def>) => boolean
>Req : Req
>Def : Def
>_table : Table<Req, Def>
>Table : Table<Req, Def>
>Req : Req
>Def : Def
>_conflictTarget : ConflictTarget<Req & Def>
>ConflictTarget : ConflictTarget<Cols>
>Req : Req
>Def : Def
throw new Error();
>new Error() : Error
>Error : ErrorConstructor
}
function f() {
>f : () => void
insertOnConflictDoNothing(bookTable, ConflictTarget.tableColumns(["serial"])); // <-- No error here; should use the type inferred for the return type of `tableColumns`
>insertOnConflictDoNothing(bookTable, ConflictTarget.tableColumns(["serial"])) : boolean
>insertOnConflictDoNothing : <Req extends object, Def extends object>(_table: Table<Req, Def>, _conflictTarget: ConflictTarget<Req & Def>) => boolean
>bookTable : Table<BookReq, BookDef>
>ConflictTarget.tableColumns(["serial"]) : ConflictTarget<BookReq & BookDef>
>ConflictTarget.tableColumns : <Cols>(cols: (keyof Cols)[]) => ConflictTarget<Cols>
>ConflictTarget : typeof ConflictTarget
>tableColumns : <Cols>(cols: (keyof Cols)[]) => ConflictTarget<Cols>
>["serial"] : "serial"[]
>"serial" : "serial"
}

View File

@ -0,0 +1,11 @@
interface X {
a: string;
b: string;
}
declare function foo<T = X>(x: keyof T, y: keyof T): T;
declare function bar<T>(x: keyof T, y: keyof T): T;
const a = foo<X>('a', 'b'); // compiles cleanly
const b = foo('a', 'b'); // also clean
const c = bar('a', 'b'); // still clean

View File

@ -0,0 +1,46 @@
// #22736
declare class Write {
protected dummy: Write;
}
declare class Col<s, a> {
protected dummy: [Col<s, a>, s, a];
}
declare class Table<Req, Def> {
protected dummy: [Table<Req, Def>, Req, Def];
}
type MakeTable<T1 extends object, T2 extends object> = {
[P in keyof T1]: Col<Write, T1[P]>;
} & {
[P in keyof T2]: Col<Write, T2[P]>;
};
declare class ConflictTarget<Cols> {
public static tableColumns<Cols>(cols: (keyof Cols)[]): ConflictTarget<Cols>;
protected dummy: [ConflictTarget<Cols>, Cols];
}
const bookTable: Table<BookReq, BookDef> = null as any
interface BookReq {
readonly title: string;
readonly serial: number;
}
interface BookDef {
readonly author: string;
readonly numPages: number | null;
}
function insertOnConflictDoNothing<Req extends object, Def extends object>(_table: Table<Req, Def>, _conflictTarget: ConflictTarget<Req & Def>): boolean {
throw new Error();
}
function f() {
insertOnConflictDoNothing(bookTable, ConflictTarget.tableColumns(["serial"])); // <-- No error here; should use the type inferred for the return type of `tableColumns`
}