Skip to content

Commit

Permalink
Merge pull request #12589 from Microsoft/mappedTypeModifierInference
Browse files Browse the repository at this point in the history
Mapped type modifier inference
  • Loading branch information
ahejlsberg authored Nov 30, 2016
2 parents 4c9bdb9 + 2517187 commit 030da0b
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 10 deletions.
14 changes: 8 additions & 6 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4532,7 +4532,7 @@ namespace ts {
const isomorphicProp = isomorphicType && getPropertyOfType(isomorphicType, propName);
const isOptional = templateOptional || !!(isomorphicProp && isomorphicProp.flags & SymbolFlags.Optional);
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);
members[propName] = prop;
}
Expand All @@ -4556,7 +4556,7 @@ namespace ts {
function getTemplateTypeFromMappedType(type: MappedType) {
return type.templateType ||
(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);
}

Expand Down Expand Up @@ -6021,7 +6021,7 @@ namespace ts {
}
const mapper = createUnaryTypeMapper(getTypeParameterFromMappedType(type), indexType);
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) {
Expand Down Expand Up @@ -8484,16 +8484,18 @@ namespace ts {
const typeInferences = createTypeInferencesObject();
const typeInferencesArray = [typeInferences];
const templateType = getTemplateTypeFromMappedType(target);
const readonlyMask = target.declaration.readonlyToken ? false : true;
const optionalMask = target.declaration.questionToken ? 0 : SymbolFlags.Optional;
const properties = getPropertiesOfType(source);
const members = createSymbolTable(properties);
let hasInferredTypes = false;
for (const prop of properties) {
const inferredPropType = inferTargetType(getTypeOfSymbol(prop));
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.type = inferredPropType;
inferredProp.isReadonly = isReadonlySymbol(prop);
inferredProp.isReadonly = readonlyMask && isReadonlySymbol(prop);
members[prop.name] = inferredProp;
hasInferredTypes = true;
}
Expand All @@ -8502,7 +8504,7 @@ namespace ts {
if (indexInfo) {
const inferredIndexType = inferTargetType(indexInfo.type);
if (inferredIndexType) {
indexInfo = createIndexInfo(inferredIndexType, indexInfo.isReadonly);
indexInfo = createIndexInfo(inferredIndexType, readonlyMask && indexInfo.isReadonly);
hasInferredTypes = true;
}
}
Expand Down
34 changes: 34 additions & 0 deletions tests/baselines/reference/isomorphicMappedTypeInference.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,21 @@ function f6(s: string) {
});
let v = unboxify(b);
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]
Expand Down Expand Up @@ -190,6 +205,11 @@ function f6(s) {
var v = unboxify(b);
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]
Expand Down Expand Up @@ -220,3 +240,17 @@ declare function makeDictionary<T>(obj: {
[x: string]: T;
};
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;
61 changes: 61 additions & 0 deletions tests/baselines/reference/isomorphicMappedTypeInference.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -332,3 +332,64 @@ function f6(s: string) {
>v : Symbol(v, Decl(isomorphicMappedTypeInference.ts, 102, 7))
>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))
}
64 changes: 64 additions & 0 deletions tests/baselines/reference/isomorphicMappedTypeInference.types
Original file line number Diff line number Diff line change
Expand Up @@ -403,3 +403,67 @@ function f6(s: string) {
>v : { [x: string]: string | number | boolean; }
>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
}
8 changes: 4 additions & 4 deletions tests/baselines/reference/mappedTypeErrors.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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 'string | number' 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(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][]; }'.


Expand Down Expand Up @@ -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] }; // 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
~
!!! 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
~
!!! 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>() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @strictNullChecks: true
// @noimplicitany: true
// @declaration: true

Expand Down Expand Up @@ -104,4 +105,19 @@ function f6(s: string) {
});
let v = unboxify(b);
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 }
}

0 comments on commit 030da0b

Please sign in to comment.