mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-15 03:23:08 -06:00
Merge pull request #29121 from Microsoft/mappedTypeConstraints
Improve constraints for non-homomorphic mapped types
This commit is contained in:
commit
8570a67572
@ -7084,6 +7084,39 @@ namespace ts {
|
||||
setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, undefined);
|
||||
}
|
||||
|
||||
// Return the lower bound of the key type in a mapped type. Intuitively, the lower
|
||||
// bound includes those keys that are known to always be present, for example because
|
||||
// because of constraints on type parameters (e.g. 'keyof T' for a constrained T).
|
||||
function getLowerBoundOfKeyType(type: Type): Type {
|
||||
if (type.flags & (TypeFlags.Any | TypeFlags.Primitive)) {
|
||||
return type;
|
||||
}
|
||||
if (type.flags & TypeFlags.Index) {
|
||||
return getIndexType(getApparentType((<IndexType>type).type));
|
||||
}
|
||||
if (type.flags & TypeFlags.Conditional) {
|
||||
return getLowerBoundOfConditionalType(<ConditionalType>type);
|
||||
}
|
||||
if (type.flags & TypeFlags.Union) {
|
||||
return getUnionType(sameMap((<UnionType>type).types, getLowerBoundOfKeyType));
|
||||
}
|
||||
if (type.flags & TypeFlags.Intersection) {
|
||||
return getIntersectionType(sameMap((<UnionType>type).types, getLowerBoundOfKeyType));
|
||||
}
|
||||
return neverType;
|
||||
}
|
||||
|
||||
function getLowerBoundOfConditionalType(type: ConditionalType) {
|
||||
if (type.root.isDistributive) {
|
||||
const constraint = getLowerBoundOfKeyType(type.checkType);
|
||||
if (constraint !== type.checkType) {
|
||||
const mapper = makeUnaryTypeMapper(type.root.checkType, constraint);
|
||||
return getConditionalTypeInstantiation(type, combineTypeMappers(mapper, type.mapper));
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
/** Resolve the members of a mapped type { [P in K]: T } */
|
||||
function resolveMappedTypeMembers(type: MappedType) {
|
||||
const members: SymbolTable = createSymbolTable();
|
||||
@ -7112,10 +7145,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
else {
|
||||
// If the key type is a 'keyof X', obtain 'keyof C' where C is the base constraint of X.
|
||||
// Then iterate over the constituents of the key type.
|
||||
const iterationType = constraintType.flags & TypeFlags.Index ? getIndexType(getApparentType((<IndexType>constraintType).type)) : constraintType;
|
||||
forEachType(iterationType, addMemberForKeyType);
|
||||
forEachType(getLowerBoundOfKeyType(constraintType), addMemberForKeyType);
|
||||
}
|
||||
setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo);
|
||||
|
||||
|
||||
70
tests/baselines/reference/mappedTypeConstraints.js
Normal file
70
tests/baselines/reference/mappedTypeConstraints.js
Normal file
@ -0,0 +1,70 @@
|
||||
//// [mappedTypeConstraints.ts]
|
||||
function f0<T extends { a: string, b: string }>(obj: Pick<T, Extract<keyof T, 'b'>>) {
|
||||
obj.b;
|
||||
}
|
||||
|
||||
function f1<T extends { a: string, b: string }>(obj: Pick<T, Exclude<keyof T, 'a'>>) {
|
||||
obj.b;
|
||||
}
|
||||
|
||||
function f2<T extends { a: string, b: string }, U extends { b: string, c: string }>(obj: Pick<T | U, keyof (T | U)>) {
|
||||
obj.b;
|
||||
}
|
||||
|
||||
function f3<T extends { a: string, b: string }, U extends { b: string, c: string }>(obj: Pick<T & U, keyof (T & U)>) {
|
||||
obj.a;
|
||||
obj.b;
|
||||
obj.c;
|
||||
}
|
||||
|
||||
function f4<T extends { a: string, b: string }>(obj: Record<Exclude<keyof T, 'b'> | 'c', string>) {
|
||||
obj.a;
|
||||
obj.c;
|
||||
}
|
||||
|
||||
// Repro from #28821
|
||||
|
||||
type TargetProps = {
|
||||
foo: string,
|
||||
bar: string
|
||||
};
|
||||
|
||||
const modifier = <T extends TargetProps>(targetProps: T) => {
|
||||
let {bar, ...rest} = targetProps;
|
||||
rest.foo;
|
||||
};
|
||||
|
||||
|
||||
//// [mappedTypeConstraints.js]
|
||||
"use strict";
|
||||
var __rest = (this && this.__rest) || function (s, e) {
|
||||
var t = {};
|
||||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
||||
t[p] = s[p];
|
||||
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
||||
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0)
|
||||
t[p[i]] = s[p[i]];
|
||||
return t;
|
||||
};
|
||||
function f0(obj) {
|
||||
obj.b;
|
||||
}
|
||||
function f1(obj) {
|
||||
obj.b;
|
||||
}
|
||||
function f2(obj) {
|
||||
obj.b;
|
||||
}
|
||||
function f3(obj) {
|
||||
obj.a;
|
||||
obj.b;
|
||||
obj.c;
|
||||
}
|
||||
function f4(obj) {
|
||||
obj.a;
|
||||
obj.c;
|
||||
}
|
||||
var modifier = function (targetProps) {
|
||||
var bar = targetProps.bar, rest = __rest(targetProps, ["bar"]);
|
||||
rest.foo;
|
||||
};
|
||||
140
tests/baselines/reference/mappedTypeConstraints.symbols
Normal file
140
tests/baselines/reference/mappedTypeConstraints.symbols
Normal file
@ -0,0 +1,140 @@
|
||||
=== tests/cases/conformance/types/mapped/mappedTypeConstraints.ts ===
|
||||
function f0<T extends { a: string, b: string }>(obj: Pick<T, Extract<keyof T, 'b'>>) {
|
||||
>f0 : Symbol(f0, Decl(mappedTypeConstraints.ts, 0, 0))
|
||||
>T : Symbol(T, Decl(mappedTypeConstraints.ts, 0, 12))
|
||||
>a : Symbol(a, Decl(mappedTypeConstraints.ts, 0, 23))
|
||||
>b : Symbol(b, Decl(mappedTypeConstraints.ts, 0, 34))
|
||||
>obj : Symbol(obj, Decl(mappedTypeConstraints.ts, 0, 48))
|
||||
>Pick : Symbol(Pick, Decl(lib.es5.d.ts, --, --))
|
||||
>T : Symbol(T, Decl(mappedTypeConstraints.ts, 0, 12))
|
||||
>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --))
|
||||
>T : Symbol(T, Decl(mappedTypeConstraints.ts, 0, 12))
|
||||
|
||||
obj.b;
|
||||
>obj.b : Symbol(b, Decl(mappedTypeConstraints.ts, 0, 34))
|
||||
>obj : Symbol(obj, Decl(mappedTypeConstraints.ts, 0, 48))
|
||||
>b : Symbol(b, Decl(mappedTypeConstraints.ts, 0, 34))
|
||||
}
|
||||
|
||||
function f1<T extends { a: string, b: string }>(obj: Pick<T, Exclude<keyof T, 'a'>>) {
|
||||
>f1 : Symbol(f1, Decl(mappedTypeConstraints.ts, 2, 1))
|
||||
>T : Symbol(T, Decl(mappedTypeConstraints.ts, 4, 12))
|
||||
>a : Symbol(a, Decl(mappedTypeConstraints.ts, 4, 23))
|
||||
>b : Symbol(b, Decl(mappedTypeConstraints.ts, 4, 34))
|
||||
>obj : Symbol(obj, Decl(mappedTypeConstraints.ts, 4, 48))
|
||||
>Pick : Symbol(Pick, Decl(lib.es5.d.ts, --, --))
|
||||
>T : Symbol(T, Decl(mappedTypeConstraints.ts, 4, 12))
|
||||
>Exclude : Symbol(Exclude, Decl(lib.es5.d.ts, --, --))
|
||||
>T : Symbol(T, Decl(mappedTypeConstraints.ts, 4, 12))
|
||||
|
||||
obj.b;
|
||||
>obj.b : Symbol(b, Decl(mappedTypeConstraints.ts, 4, 34))
|
||||
>obj : Symbol(obj, Decl(mappedTypeConstraints.ts, 4, 48))
|
||||
>b : Symbol(b, Decl(mappedTypeConstraints.ts, 4, 34))
|
||||
}
|
||||
|
||||
function f2<T extends { a: string, b: string }, U extends { b: string, c: string }>(obj: Pick<T | U, keyof (T | U)>) {
|
||||
>f2 : Symbol(f2, Decl(mappedTypeConstraints.ts, 6, 1))
|
||||
>T : Symbol(T, Decl(mappedTypeConstraints.ts, 8, 12))
|
||||
>a : Symbol(a, Decl(mappedTypeConstraints.ts, 8, 23))
|
||||
>b : Symbol(b, Decl(mappedTypeConstraints.ts, 8, 34))
|
||||
>U : Symbol(U, Decl(mappedTypeConstraints.ts, 8, 47))
|
||||
>b : Symbol(b, Decl(mappedTypeConstraints.ts, 8, 59))
|
||||
>c : Symbol(c, Decl(mappedTypeConstraints.ts, 8, 70))
|
||||
>obj : Symbol(obj, Decl(mappedTypeConstraints.ts, 8, 84))
|
||||
>Pick : Symbol(Pick, Decl(lib.es5.d.ts, --, --))
|
||||
>T : Symbol(T, Decl(mappedTypeConstraints.ts, 8, 12))
|
||||
>U : Symbol(U, Decl(mappedTypeConstraints.ts, 8, 47))
|
||||
>T : Symbol(T, Decl(mappedTypeConstraints.ts, 8, 12))
|
||||
>U : Symbol(U, Decl(mappedTypeConstraints.ts, 8, 47))
|
||||
|
||||
obj.b;
|
||||
>obj.b : Symbol(b, Decl(mappedTypeConstraints.ts, 8, 34), Decl(mappedTypeConstraints.ts, 8, 59))
|
||||
>obj : Symbol(obj, Decl(mappedTypeConstraints.ts, 8, 84))
|
||||
>b : Symbol(b, Decl(mappedTypeConstraints.ts, 8, 34), Decl(mappedTypeConstraints.ts, 8, 59))
|
||||
}
|
||||
|
||||
function f3<T extends { a: string, b: string }, U extends { b: string, c: string }>(obj: Pick<T & U, keyof (T & U)>) {
|
||||
>f3 : Symbol(f3, Decl(mappedTypeConstraints.ts, 10, 1))
|
||||
>T : Symbol(T, Decl(mappedTypeConstraints.ts, 12, 12))
|
||||
>a : Symbol(a, Decl(mappedTypeConstraints.ts, 12, 23))
|
||||
>b : Symbol(b, Decl(mappedTypeConstraints.ts, 12, 34))
|
||||
>U : Symbol(U, Decl(mappedTypeConstraints.ts, 12, 47))
|
||||
>b : Symbol(b, Decl(mappedTypeConstraints.ts, 12, 59))
|
||||
>c : Symbol(c, Decl(mappedTypeConstraints.ts, 12, 70))
|
||||
>obj : Symbol(obj, Decl(mappedTypeConstraints.ts, 12, 84))
|
||||
>Pick : Symbol(Pick, Decl(lib.es5.d.ts, --, --))
|
||||
>T : Symbol(T, Decl(mappedTypeConstraints.ts, 12, 12))
|
||||
>U : Symbol(U, Decl(mappedTypeConstraints.ts, 12, 47))
|
||||
>T : Symbol(T, Decl(mappedTypeConstraints.ts, 12, 12))
|
||||
>U : Symbol(U, Decl(mappedTypeConstraints.ts, 12, 47))
|
||||
|
||||
obj.a;
|
||||
>obj.a : Symbol(a, Decl(mappedTypeConstraints.ts, 12, 23))
|
||||
>obj : Symbol(obj, Decl(mappedTypeConstraints.ts, 12, 84))
|
||||
>a : Symbol(a, Decl(mappedTypeConstraints.ts, 12, 23))
|
||||
|
||||
obj.b;
|
||||
>obj.b : Symbol(b, Decl(mappedTypeConstraints.ts, 12, 34), Decl(mappedTypeConstraints.ts, 12, 59))
|
||||
>obj : Symbol(obj, Decl(mappedTypeConstraints.ts, 12, 84))
|
||||
>b : Symbol(b, Decl(mappedTypeConstraints.ts, 12, 34), Decl(mappedTypeConstraints.ts, 12, 59))
|
||||
|
||||
obj.c;
|
||||
>obj.c : Symbol(c, Decl(mappedTypeConstraints.ts, 12, 70))
|
||||
>obj : Symbol(obj, Decl(mappedTypeConstraints.ts, 12, 84))
|
||||
>c : Symbol(c, Decl(mappedTypeConstraints.ts, 12, 70))
|
||||
}
|
||||
|
||||
function f4<T extends { a: string, b: string }>(obj: Record<Exclude<keyof T, 'b'> | 'c', string>) {
|
||||
>f4 : Symbol(f4, Decl(mappedTypeConstraints.ts, 16, 1))
|
||||
>T : Symbol(T, Decl(mappedTypeConstraints.ts, 18, 12))
|
||||
>a : Symbol(a, Decl(mappedTypeConstraints.ts, 18, 23))
|
||||
>b : Symbol(b, Decl(mappedTypeConstraints.ts, 18, 34))
|
||||
>obj : Symbol(obj, Decl(mappedTypeConstraints.ts, 18, 48))
|
||||
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
|
||||
>Exclude : Symbol(Exclude, Decl(lib.es5.d.ts, --, --))
|
||||
>T : Symbol(T, Decl(mappedTypeConstraints.ts, 18, 12))
|
||||
|
||||
obj.a;
|
||||
>obj.a : Symbol(a)
|
||||
>obj : Symbol(obj, Decl(mappedTypeConstraints.ts, 18, 48))
|
||||
>a : Symbol(a)
|
||||
|
||||
obj.c;
|
||||
>obj.c : Symbol(c)
|
||||
>obj : Symbol(obj, Decl(mappedTypeConstraints.ts, 18, 48))
|
||||
>c : Symbol(c)
|
||||
}
|
||||
|
||||
// Repro from #28821
|
||||
|
||||
type TargetProps = {
|
||||
>TargetProps : Symbol(TargetProps, Decl(mappedTypeConstraints.ts, 21, 1))
|
||||
|
||||
foo: string,
|
||||
>foo : Symbol(foo, Decl(mappedTypeConstraints.ts, 25, 20))
|
||||
|
||||
bar: string
|
||||
>bar : Symbol(bar, Decl(mappedTypeConstraints.ts, 26, 16))
|
||||
|
||||
};
|
||||
|
||||
const modifier = <T extends TargetProps>(targetProps: T) => {
|
||||
>modifier : Symbol(modifier, Decl(mappedTypeConstraints.ts, 30, 5))
|
||||
>T : Symbol(T, Decl(mappedTypeConstraints.ts, 30, 18))
|
||||
>TargetProps : Symbol(TargetProps, Decl(mappedTypeConstraints.ts, 21, 1))
|
||||
>targetProps : Symbol(targetProps, Decl(mappedTypeConstraints.ts, 30, 41))
|
||||
>T : Symbol(T, Decl(mappedTypeConstraints.ts, 30, 18))
|
||||
|
||||
let {bar, ...rest} = targetProps;
|
||||
>bar : Symbol(bar, Decl(mappedTypeConstraints.ts, 31, 9))
|
||||
>rest : Symbol(rest, Decl(mappedTypeConstraints.ts, 31, 13))
|
||||
>targetProps : Symbol(targetProps, Decl(mappedTypeConstraints.ts, 30, 41))
|
||||
|
||||
rest.foo;
|
||||
>rest.foo : Symbol(foo, Decl(mappedTypeConstraints.ts, 25, 20))
|
||||
>rest : Symbol(rest, Decl(mappedTypeConstraints.ts, 31, 13))
|
||||
>foo : Symbol(foo, Decl(mappedTypeConstraints.ts, 25, 20))
|
||||
|
||||
};
|
||||
|
||||
110
tests/baselines/reference/mappedTypeConstraints.types
Normal file
110
tests/baselines/reference/mappedTypeConstraints.types
Normal file
@ -0,0 +1,110 @@
|
||||
=== tests/cases/conformance/types/mapped/mappedTypeConstraints.ts ===
|
||||
function f0<T extends { a: string, b: string }>(obj: Pick<T, Extract<keyof T, 'b'>>) {
|
||||
>f0 : <T extends { a: string; b: string; }>(obj: Pick<T, Extract<keyof T, "b">>) => void
|
||||
>a : string
|
||||
>b : string
|
||||
>obj : Pick<T, Extract<keyof T, "b">>
|
||||
|
||||
obj.b;
|
||||
>obj.b : T["b"]
|
||||
>obj : Pick<T, Extract<keyof T, "b">>
|
||||
>b : T["b"]
|
||||
}
|
||||
|
||||
function f1<T extends { a: string, b: string }>(obj: Pick<T, Exclude<keyof T, 'a'>>) {
|
||||
>f1 : <T extends { a: string; b: string; }>(obj: Pick<T, Exclude<keyof T, "a">>) => void
|
||||
>a : string
|
||||
>b : string
|
||||
>obj : Pick<T, Exclude<keyof T, "a">>
|
||||
|
||||
obj.b;
|
||||
>obj.b : T["b"]
|
||||
>obj : Pick<T, Exclude<keyof T, "a">>
|
||||
>b : T["b"]
|
||||
}
|
||||
|
||||
function f2<T extends { a: string, b: string }, U extends { b: string, c: string }>(obj: Pick<T | U, keyof (T | U)>) {
|
||||
>f2 : <T extends { a: string; b: string; }, U extends { b: string; c: string; }>(obj: Pick<T | U, keyof T & keyof U>) => void
|
||||
>a : string
|
||||
>b : string
|
||||
>b : string
|
||||
>c : string
|
||||
>obj : Pick<T | U, keyof T & keyof U>
|
||||
|
||||
obj.b;
|
||||
>obj.b : (T | U)["b"]
|
||||
>obj : Pick<T | U, keyof T & keyof U>
|
||||
>b : (T | U)["b"]
|
||||
}
|
||||
|
||||
function f3<T extends { a: string, b: string }, U extends { b: string, c: string }>(obj: Pick<T & U, keyof (T & U)>) {
|
||||
>f3 : <T extends { a: string; b: string; }, U extends { b: string; c: string; }>(obj: Pick<T & U, keyof T | keyof U>) => void
|
||||
>a : string
|
||||
>b : string
|
||||
>b : string
|
||||
>c : string
|
||||
>obj : Pick<T & U, keyof T | keyof U>
|
||||
|
||||
obj.a;
|
||||
>obj.a : (T & U)["a"]
|
||||
>obj : Pick<T & U, keyof T | keyof U>
|
||||
>a : (T & U)["a"]
|
||||
|
||||
obj.b;
|
||||
>obj.b : (T & U)["b"]
|
||||
>obj : Pick<T & U, keyof T | keyof U>
|
||||
>b : (T & U)["b"]
|
||||
|
||||
obj.c;
|
||||
>obj.c : (T & U)["c"]
|
||||
>obj : Pick<T & U, keyof T | keyof U>
|
||||
>c : (T & U)["c"]
|
||||
}
|
||||
|
||||
function f4<T extends { a: string, b: string }>(obj: Record<Exclude<keyof T, 'b'> | 'c', string>) {
|
||||
>f4 : <T extends { a: string; b: string; }>(obj: Record<"c" | Exclude<keyof T, "b">, string>) => void
|
||||
>a : string
|
||||
>b : string
|
||||
>obj : Record<"c" | Exclude<keyof T, "b">, string>
|
||||
|
||||
obj.a;
|
||||
>obj.a : string
|
||||
>obj : Record<"c" | Exclude<keyof T, "b">, string>
|
||||
>a : string
|
||||
|
||||
obj.c;
|
||||
>obj.c : string
|
||||
>obj : Record<"c" | Exclude<keyof T, "b">, string>
|
||||
>c : string
|
||||
}
|
||||
|
||||
// Repro from #28821
|
||||
|
||||
type TargetProps = {
|
||||
>TargetProps : TargetProps
|
||||
|
||||
foo: string,
|
||||
>foo : string
|
||||
|
||||
bar: string
|
||||
>bar : string
|
||||
|
||||
};
|
||||
|
||||
const modifier = <T extends TargetProps>(targetProps: T) => {
|
||||
>modifier : <T extends TargetProps>(targetProps: T) => void
|
||||
><T extends TargetProps>(targetProps: T) => { let {bar, ...rest} = targetProps; rest.foo;} : <T extends TargetProps>(targetProps: T) => void
|
||||
>targetProps : T
|
||||
|
||||
let {bar, ...rest} = targetProps;
|
||||
>bar : string
|
||||
>rest : Pick<T, Exclude<keyof T, "bar">>
|
||||
>targetProps : T
|
||||
|
||||
rest.foo;
|
||||
>rest.foo : T["foo"]
|
||||
>rest : Pick<T, Exclude<keyof T, "bar">>
|
||||
>foo : T["foo"]
|
||||
|
||||
};
|
||||
|
||||
@ -0,0 +1,36 @@
|
||||
// @strict: true
|
||||
|
||||
function f0<T extends { a: string, b: string }>(obj: Pick<T, Extract<keyof T, 'b'>>) {
|
||||
obj.b;
|
||||
}
|
||||
|
||||
function f1<T extends { a: string, b: string }>(obj: Pick<T, Exclude<keyof T, 'a'>>) {
|
||||
obj.b;
|
||||
}
|
||||
|
||||
function f2<T extends { a: string, b: string }, U extends { b: string, c: string }>(obj: Pick<T | U, keyof (T | U)>) {
|
||||
obj.b;
|
||||
}
|
||||
|
||||
function f3<T extends { a: string, b: string }, U extends { b: string, c: string }>(obj: Pick<T & U, keyof (T & U)>) {
|
||||
obj.a;
|
||||
obj.b;
|
||||
obj.c;
|
||||
}
|
||||
|
||||
function f4<T extends { a: string, b: string }>(obj: Record<Exclude<keyof T, 'b'> | 'c', string>) {
|
||||
obj.a;
|
||||
obj.c;
|
||||
}
|
||||
|
||||
// Repro from #28821
|
||||
|
||||
type TargetProps = {
|
||||
foo: string,
|
||||
bar: string
|
||||
};
|
||||
|
||||
const modifier = <T extends TargetProps>(targetProps: T) => {
|
||||
let {bar, ...rest} = targetProps;
|
||||
rest.foo;
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user