Merge pull request #12589 from Microsoft/mappedTypeModifierInference
Mapped type modifier inference
This commit is contained in:
commit
030da0b69f
|
@ -4532,7 +4532,7 @@ namespace ts {
|
||||||
const isomorphicProp = isomorphicType && getPropertyOfType(isomorphicType, propName);
|
const isomorphicProp = isomorphicType && getPropertyOfType(isomorphicType, propName);
|
||||||
const isOptional = templateOptional || !!(isomorphicProp && isomorphicProp.flags & SymbolFlags.Optional);
|
const isOptional = templateOptional || !!(isomorphicProp && isomorphicProp.flags & SymbolFlags.Optional);
|
||||||
const prop = <TransientSymbol>createSymbol(SymbolFlags.Property | SymbolFlags.Transient | (isOptional ? SymbolFlags.Optional : 0), propName);
|
const prop = <TransientSymbol>createSymbol(SymbolFlags.Property | SymbolFlags.Transient | (isOptional ? SymbolFlags.Optional : 0), propName);
|
||||||
prop.type = addOptionality(propType, isOptional);
|
prop.type = propType;
|
||||||
prop.isReadonly = templateReadonly || isomorphicProp && isReadonlySymbol(isomorphicProp);
|
prop.isReadonly = templateReadonly || isomorphicProp && isReadonlySymbol(isomorphicProp);
|
||||||
members[propName] = prop;
|
members[propName] = prop;
|
||||||
}
|
}
|
||||||
|
@ -4556,7 +4556,7 @@ namespace ts {
|
||||||
function getTemplateTypeFromMappedType(type: MappedType) {
|
function getTemplateTypeFromMappedType(type: MappedType) {
|
||||||
return type.templateType ||
|
return type.templateType ||
|
||||||
(type.templateType = type.declaration.type ?
|
(type.templateType = type.declaration.type ?
|
||||||
instantiateType(getTypeFromTypeNode(type.declaration.type), type.mapper || identityMapper) :
|
instantiateType(addOptionality(getTypeFromTypeNode(type.declaration.type), !!type.declaration.questionToken), type.mapper || identityMapper) :
|
||||||
unknownType);
|
unknownType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6021,7 +6021,7 @@ namespace ts {
|
||||||
}
|
}
|
||||||
const mapper = createUnaryTypeMapper(getTypeParameterFromMappedType(type), indexType);
|
const mapper = createUnaryTypeMapper(getTypeParameterFromMappedType(type), indexType);
|
||||||
const templateMapper = type.mapper ? combineTypeMappers(type.mapper, mapper) : mapper;
|
const templateMapper = type.mapper ? combineTypeMappers(type.mapper, mapper) : mapper;
|
||||||
return addOptionality(instantiateType(getTemplateTypeFromMappedType(type), templateMapper), !!type.declaration.questionToken);
|
return instantiateType(getTemplateTypeFromMappedType(type), templateMapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode) {
|
function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode) {
|
||||||
|
@ -8484,16 +8484,18 @@ namespace ts {
|
||||||
const typeInferences = createTypeInferencesObject();
|
const typeInferences = createTypeInferencesObject();
|
||||||
const typeInferencesArray = [typeInferences];
|
const typeInferencesArray = [typeInferences];
|
||||||
const templateType = getTemplateTypeFromMappedType(target);
|
const templateType = getTemplateTypeFromMappedType(target);
|
||||||
|
const readonlyMask = target.declaration.readonlyToken ? false : true;
|
||||||
|
const optionalMask = target.declaration.questionToken ? 0 : SymbolFlags.Optional;
|
||||||
const properties = getPropertiesOfType(source);
|
const properties = getPropertiesOfType(source);
|
||||||
const members = createSymbolTable(properties);
|
const members = createSymbolTable(properties);
|
||||||
let hasInferredTypes = false;
|
let hasInferredTypes = false;
|
||||||
for (const prop of properties) {
|
for (const prop of properties) {
|
||||||
const inferredPropType = inferTargetType(getTypeOfSymbol(prop));
|
const inferredPropType = inferTargetType(getTypeOfSymbol(prop));
|
||||||
if (inferredPropType) {
|
if (inferredPropType) {
|
||||||
const inferredProp = <TransientSymbol>createSymbol(SymbolFlags.Property | SymbolFlags.Transient | prop.flags & SymbolFlags.Optional, prop.name);
|
const inferredProp = <TransientSymbol>createSymbol(SymbolFlags.Property | SymbolFlags.Transient | prop.flags & optionalMask, prop.name);
|
||||||
inferredProp.declarations = prop.declarations;
|
inferredProp.declarations = prop.declarations;
|
||||||
inferredProp.type = inferredPropType;
|
inferredProp.type = inferredPropType;
|
||||||
inferredProp.isReadonly = isReadonlySymbol(prop);
|
inferredProp.isReadonly = readonlyMask && isReadonlySymbol(prop);
|
||||||
members[prop.name] = inferredProp;
|
members[prop.name] = inferredProp;
|
||||||
hasInferredTypes = true;
|
hasInferredTypes = true;
|
||||||
}
|
}
|
||||||
|
@ -8502,7 +8504,7 @@ namespace ts {
|
||||||
if (indexInfo) {
|
if (indexInfo) {
|
||||||
const inferredIndexType = inferTargetType(indexInfo.type);
|
const inferredIndexType = inferTargetType(indexInfo.type);
|
||||||
if (inferredIndexType) {
|
if (inferredIndexType) {
|
||||||
indexInfo = createIndexInfo(inferredIndexType, indexInfo.isReadonly);
|
indexInfo = createIndexInfo(inferredIndexType, readonlyMask && indexInfo.isReadonly);
|
||||||
hasInferredTypes = true;
|
hasInferredTypes = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,6 +105,21 @@ function f6(s: string) {
|
||||||
let x: string | number | boolean = v[s];
|
let x: string | number | boolean = v[s];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare function validate<T>(obj: { [P in keyof T]?: T[P] }): T;
|
||||||
|
declare function clone<T>(obj: { readonly [P in keyof T]: T[P] }): T;
|
||||||
|
declare function validateAndClone<T>(obj: { readonly [P in keyof T]?: T[P] }): T;
|
||||||
|
|
||||||
|
type Foo = {
|
||||||
|
a?: number;
|
||||||
|
readonly b: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function f10(foo: Foo) {
|
||||||
|
let x = validate(foo); // { a: number, readonly b: string }
|
||||||
|
let y = clone(foo); // { a?: number, b: string }
|
||||||
|
let z = validateAndClone(foo); // { a: number, b: string }
|
||||||
|
}
|
||||||
|
|
||||||
//// [isomorphicMappedTypeInference.js]
|
//// [isomorphicMappedTypeInference.js]
|
||||||
function box(x) {
|
function box(x) {
|
||||||
return { value: x };
|
return { value: x };
|
||||||
|
@ -190,6 +205,11 @@ function f6(s) {
|
||||||
var v = unboxify(b);
|
var v = unboxify(b);
|
||||||
var x = v[s];
|
var x = v[s];
|
||||||
}
|
}
|
||||||
|
function f10(foo) {
|
||||||
|
var x = validate(foo); // { a: number, readonly b: string }
|
||||||
|
var y = clone(foo); // { a?: number, b: string }
|
||||||
|
var z = validateAndClone(foo); // { a: number, b: string }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//// [isomorphicMappedTypeInference.d.ts]
|
//// [isomorphicMappedTypeInference.d.ts]
|
||||||
|
@ -220,3 +240,17 @@ declare function makeDictionary<T>(obj: {
|
||||||
[x: string]: T;
|
[x: string]: T;
|
||||||
};
|
};
|
||||||
declare function f6(s: string): void;
|
declare function f6(s: string): void;
|
||||||
|
declare function validate<T>(obj: {
|
||||||
|
[P in keyof T]?: T[P];
|
||||||
|
}): T;
|
||||||
|
declare function clone<T>(obj: {
|
||||||
|
readonly [P in keyof T]: T[P];
|
||||||
|
}): T;
|
||||||
|
declare function validateAndClone<T>(obj: {
|
||||||
|
readonly [P in keyof T]?: T[P];
|
||||||
|
}): T;
|
||||||
|
declare type Foo = {
|
||||||
|
a?: number;
|
||||||
|
readonly b: string;
|
||||||
|
};
|
||||||
|
declare function f10(foo: Foo): void;
|
||||||
|
|
|
@ -332,3 +332,64 @@ function f6(s: string) {
|
||||||
>v : Symbol(v, Decl(isomorphicMappedTypeInference.ts, 102, 7))
|
>v : Symbol(v, Decl(isomorphicMappedTypeInference.ts, 102, 7))
|
||||||
>s : Symbol(s, Decl(isomorphicMappedTypeInference.ts, 96, 12))
|
>s : Symbol(s, Decl(isomorphicMappedTypeInference.ts, 96, 12))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare function validate<T>(obj: { [P in keyof T]?: T[P] }): T;
|
||||||
|
>validate : Symbol(validate, Decl(isomorphicMappedTypeInference.ts, 104, 1))
|
||||||
|
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 106, 26))
|
||||||
|
>obj : Symbol(obj, Decl(isomorphicMappedTypeInference.ts, 106, 29))
|
||||||
|
>P : Symbol(P, Decl(isomorphicMappedTypeInference.ts, 106, 37))
|
||||||
|
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 106, 26))
|
||||||
|
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 106, 26))
|
||||||
|
>P : Symbol(P, Decl(isomorphicMappedTypeInference.ts, 106, 37))
|
||||||
|
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 106, 26))
|
||||||
|
|
||||||
|
declare function clone<T>(obj: { readonly [P in keyof T]: T[P] }): T;
|
||||||
|
>clone : Symbol(clone, Decl(isomorphicMappedTypeInference.ts, 106, 64))
|
||||||
|
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 107, 23))
|
||||||
|
>obj : Symbol(obj, Decl(isomorphicMappedTypeInference.ts, 107, 26))
|
||||||
|
>P : Symbol(P, Decl(isomorphicMappedTypeInference.ts, 107, 43))
|
||||||
|
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 107, 23))
|
||||||
|
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 107, 23))
|
||||||
|
>P : Symbol(P, Decl(isomorphicMappedTypeInference.ts, 107, 43))
|
||||||
|
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 107, 23))
|
||||||
|
|
||||||
|
declare function validateAndClone<T>(obj: { readonly [P in keyof T]?: T[P] }): T;
|
||||||
|
>validateAndClone : Symbol(validateAndClone, Decl(isomorphicMappedTypeInference.ts, 107, 69))
|
||||||
|
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 108, 34))
|
||||||
|
>obj : Symbol(obj, Decl(isomorphicMappedTypeInference.ts, 108, 37))
|
||||||
|
>P : Symbol(P, Decl(isomorphicMappedTypeInference.ts, 108, 54))
|
||||||
|
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 108, 34))
|
||||||
|
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 108, 34))
|
||||||
|
>P : Symbol(P, Decl(isomorphicMappedTypeInference.ts, 108, 54))
|
||||||
|
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 108, 34))
|
||||||
|
|
||||||
|
type Foo = {
|
||||||
|
>Foo : Symbol(Foo, Decl(isomorphicMappedTypeInference.ts, 108, 81))
|
||||||
|
|
||||||
|
a?: number;
|
||||||
|
>a : Symbol(a, Decl(isomorphicMappedTypeInference.ts, 110, 12))
|
||||||
|
|
||||||
|
readonly b: string;
|
||||||
|
>b : Symbol(b, Decl(isomorphicMappedTypeInference.ts, 111, 15))
|
||||||
|
}
|
||||||
|
|
||||||
|
function f10(foo: Foo) {
|
||||||
|
>f10 : Symbol(f10, Decl(isomorphicMappedTypeInference.ts, 113, 1))
|
||||||
|
>foo : Symbol(foo, Decl(isomorphicMappedTypeInference.ts, 115, 13))
|
||||||
|
>Foo : Symbol(Foo, Decl(isomorphicMappedTypeInference.ts, 108, 81))
|
||||||
|
|
||||||
|
let x = validate(foo); // { a: number, readonly b: string }
|
||||||
|
>x : Symbol(x, Decl(isomorphicMappedTypeInference.ts, 116, 7))
|
||||||
|
>validate : Symbol(validate, Decl(isomorphicMappedTypeInference.ts, 104, 1))
|
||||||
|
>foo : Symbol(foo, Decl(isomorphicMappedTypeInference.ts, 115, 13))
|
||||||
|
|
||||||
|
let y = clone(foo); // { a?: number, b: string }
|
||||||
|
>y : Symbol(y, Decl(isomorphicMappedTypeInference.ts, 117, 7))
|
||||||
|
>clone : Symbol(clone, Decl(isomorphicMappedTypeInference.ts, 106, 64))
|
||||||
|
>foo : Symbol(foo, Decl(isomorphicMappedTypeInference.ts, 115, 13))
|
||||||
|
|
||||||
|
let z = validateAndClone(foo); // { a: number, b: string }
|
||||||
|
>z : Symbol(z, Decl(isomorphicMappedTypeInference.ts, 118, 7))
|
||||||
|
>validateAndClone : Symbol(validateAndClone, Decl(isomorphicMappedTypeInference.ts, 107, 69))
|
||||||
|
>foo : Symbol(foo, Decl(isomorphicMappedTypeInference.ts, 115, 13))
|
||||||
|
}
|
||||||
|
|
|
@ -403,3 +403,67 @@ function f6(s: string) {
|
||||||
>v : { [x: string]: string | number | boolean; }
|
>v : { [x: string]: string | number | boolean; }
|
||||||
>s : string
|
>s : string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare function validate<T>(obj: { [P in keyof T]?: T[P] }): T;
|
||||||
|
>validate : <T>(obj: { [P in keyof T]?: T[P] | undefined; }) => T
|
||||||
|
>T : T
|
||||||
|
>obj : { [P in keyof T]?: T[P] | undefined; }
|
||||||
|
>P : P
|
||||||
|
>T : T
|
||||||
|
>T : T
|
||||||
|
>P : P
|
||||||
|
>T : T
|
||||||
|
|
||||||
|
declare function clone<T>(obj: { readonly [P in keyof T]: T[P] }): T;
|
||||||
|
>clone : <T>(obj: { readonly [P in keyof T]: T[P]; }) => T
|
||||||
|
>T : T
|
||||||
|
>obj : { readonly [P in keyof T]: T[P]; }
|
||||||
|
>P : P
|
||||||
|
>T : T
|
||||||
|
>T : T
|
||||||
|
>P : P
|
||||||
|
>T : T
|
||||||
|
|
||||||
|
declare function validateAndClone<T>(obj: { readonly [P in keyof T]?: T[P] }): T;
|
||||||
|
>validateAndClone : <T>(obj: { readonly [P in keyof T]?: T[P] | undefined; }) => T
|
||||||
|
>T : T
|
||||||
|
>obj : { readonly [P in keyof T]?: T[P] | undefined; }
|
||||||
|
>P : P
|
||||||
|
>T : T
|
||||||
|
>T : T
|
||||||
|
>P : P
|
||||||
|
>T : T
|
||||||
|
|
||||||
|
type Foo = {
|
||||||
|
>Foo : Foo
|
||||||
|
|
||||||
|
a?: number;
|
||||||
|
>a : number | undefined
|
||||||
|
|
||||||
|
readonly b: string;
|
||||||
|
>b : string
|
||||||
|
}
|
||||||
|
|
||||||
|
function f10(foo: Foo) {
|
||||||
|
>f10 : (foo: Foo) => void
|
||||||
|
>foo : Foo
|
||||||
|
>Foo : Foo
|
||||||
|
|
||||||
|
let x = validate(foo); // { a: number, readonly b: string }
|
||||||
|
>x : { a: number; readonly b: string; }
|
||||||
|
>validate(foo) : { a: number; readonly b: string; }
|
||||||
|
>validate : <T>(obj: { [P in keyof T]?: T[P] | undefined; }) => T
|
||||||
|
>foo : Foo
|
||||||
|
|
||||||
|
let y = clone(foo); // { a?: number, b: string }
|
||||||
|
>y : { a?: number | undefined; b: string; }
|
||||||
|
>clone(foo) : { a?: number | undefined; b: string; }
|
||||||
|
>clone : <T>(obj: { readonly [P in keyof T]: T[P]; }) => T
|
||||||
|
>foo : Foo
|
||||||
|
|
||||||
|
let z = validateAndClone(foo); // { a: number, b: string }
|
||||||
|
>z : { a: number; b: string; }
|
||||||
|
>validateAndClone(foo) : { a: number; b: string; }
|
||||||
|
>validateAndClone : <T>(obj: { readonly [P in keyof T]?: T[P] | undefined; }) => T
|
||||||
|
>foo : Foo
|
||||||
|
}
|
||||||
|
|
|
@ -16,9 +16,9 @@ tests/cases/conformance/types/mapped/mappedTypeErrors.ts(38,24): error TS2344: T
|
||||||
Type 'T' is not assignable to type '"visible"'.
|
Type 'T' is not assignable to type '"visible"'.
|
||||||
Type 'string | number' is not assignable to type '"visible"'.
|
Type 'string | number' is not assignable to type '"visible"'.
|
||||||
Type 'string' is not assignable to type '"visible"'.
|
Type 'string' is not assignable to type '"visible"'.
|
||||||
tests/cases/conformance/types/mapped/mappedTypeErrors.ts(60,9): error TS2403: Subsequent variable declarations must have the same type. Variable 'x' must be of type '{ [P in keyof T]: T[P]; }', but here has type '{ [P in keyof T]?: T[P]; }'.
|
tests/cases/conformance/types/mapped/mappedTypeErrors.ts(60,9): error TS2403: Subsequent variable declarations must have the same type. Variable 'x' must be of type '{ [P in keyof T]: T[P]; }', but here has type '{ [P in keyof T]?: T[P] | undefined; }'.
|
||||||
tests/cases/conformance/types/mapped/mappedTypeErrors.ts(61,9): error TS2403: Subsequent variable declarations must have the same type. Variable 'x' must be of type '{ [P in keyof T]: T[P]; }', but here has type '{ readonly [P in keyof T]: T[P]; }'.
|
tests/cases/conformance/types/mapped/mappedTypeErrors.ts(61,9): error TS2403: Subsequent variable declarations must have the same type. Variable 'x' must be of type '{ [P in keyof T]: T[P]; }', but here has type '{ readonly [P in keyof T]: T[P]; }'.
|
||||||
tests/cases/conformance/types/mapped/mappedTypeErrors.ts(62,9): error TS2403: Subsequent variable declarations must have the same type. Variable 'x' must be of type '{ [P in keyof T]: T[P]; }', but here has type '{ readonly [P in keyof T]?: T[P]; }'.
|
tests/cases/conformance/types/mapped/mappedTypeErrors.ts(62,9): error TS2403: Subsequent variable declarations must have the same type. Variable 'x' must be of type '{ [P in keyof T]: T[P]; }', but here has type '{ readonly [P in keyof T]?: T[P] | undefined; }'.
|
||||||
tests/cases/conformance/types/mapped/mappedTypeErrors.ts(67,9): error TS2403: Subsequent variable declarations must have the same type. Variable 'x' must be of type '{ [P in keyof T]: T[P]; }', but here has type '{ [P in keyof T]: T[P][]; }'.
|
tests/cases/conformance/types/mapped/mappedTypeErrors.ts(67,9): error TS2403: Subsequent variable declarations must have the same type. Variable 'x' must be of type '{ [P in keyof T]: T[P]; }', but here has type '{ [P in keyof T]: T[P][]; }'.
|
||||||
|
|
||||||
|
|
||||||
|
@ -112,13 +112,13 @@ tests/cases/conformance/types/mapped/mappedTypeErrors.ts(67,9): error TS2403: Su
|
||||||
var x: { [P in keyof T]: T[P] };
|
var x: { [P in keyof T]: T[P] };
|
||||||
var x: { [P in keyof T]?: T[P] }; // Error
|
var x: { [P in keyof T]?: T[P] }; // Error
|
||||||
~
|
~
|
||||||
!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'x' must be of type '{ [P in keyof T]: T[P]; }', but here has type '{ [P in keyof T]?: T[P]; }'.
|
!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'x' must be of type '{ [P in keyof T]: T[P]; }', but here has type '{ [P in keyof T]?: T[P] | undefined; }'.
|
||||||
var x: { readonly [P in keyof T]: T[P] }; // Error
|
var x: { readonly [P in keyof T]: T[P] }; // Error
|
||||||
~
|
~
|
||||||
!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'x' must be of type '{ [P in keyof T]: T[P]; }', but here has type '{ readonly [P in keyof T]: T[P]; }'.
|
!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'x' must be of type '{ [P in keyof T]: T[P]; }', but here has type '{ readonly [P in keyof T]: T[P]; }'.
|
||||||
var x: { readonly [P in keyof T]?: T[P] }; // Error
|
var x: { readonly [P in keyof T]?: T[P] }; // Error
|
||||||
~
|
~
|
||||||
!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'x' must be of type '{ [P in keyof T]: T[P]; }', but here has type '{ readonly [P in keyof T]?: T[P]; }'.
|
!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'x' must be of type '{ [P in keyof T]: T[P]; }', but here has type '{ readonly [P in keyof T]?: T[P] | undefined; }'.
|
||||||
}
|
}
|
||||||
|
|
||||||
function f12<T>() {
|
function f12<T>() {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @strictNullChecks: true
|
||||||
// @noimplicitany: true
|
// @noimplicitany: true
|
||||||
// @declaration: true
|
// @declaration: true
|
||||||
|
|
||||||
|
@ -105,3 +106,18 @@ function f6(s: string) {
|
||||||
let v = unboxify(b);
|
let v = unboxify(b);
|
||||||
let x: string | number | boolean = v[s];
|
let x: string | number | boolean = v[s];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare function validate<T>(obj: { [P in keyof T]?: T[P] }): T;
|
||||||
|
declare function clone<T>(obj: { readonly [P in keyof T]: T[P] }): T;
|
||||||
|
declare function validateAndClone<T>(obj: { readonly [P in keyof T]?: T[P] }): T;
|
||||||
|
|
||||||
|
type Foo = {
|
||||||
|
a?: number;
|
||||||
|
readonly b: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function f10(foo: Foo) {
|
||||||
|
let x = validate(foo); // { a: number, readonly b: string }
|
||||||
|
let y = clone(foo); // { a?: number, b: string }
|
||||||
|
let z = validateAndClone(foo); // { a: number, b: string }
|
||||||
|
}
|
Loading…
Reference in a new issue