Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
7cd39e3
Parsing of mapped types
ahejlsberg Nov 3, 2016
d1a8af5
Parse '[P in K]' part of mapped type as a type parameter declaration
ahejlsberg Nov 3, 2016
fc450a2
Introduce MappedType in type checker
ahejlsberg Nov 4, 2016
ecdb74c
Merge branch 'master' into mappedTypes
ahejlsberg Nov 4, 2016
7807ac9
Attach symbols to mapped types
ahejlsberg Nov 4, 2016
1c7b397
Introduce instantiateCached function
ahejlsberg Nov 5, 2016
507ab30
Handle readonly and optional properties + index signatures
ahejlsberg Nov 6, 2016
2564e1c
Handle recursion in mapped type display
ahejlsberg Nov 6, 2016
5de63a7
Validate constraint type in mapped type
ahejlsberg Nov 7, 2016
de93876
Correct symbol display for type parameter of mapped type
ahejlsberg Nov 7, 2016
9f3aa38
Improve sharing by re-instantiating top level type aliases
ahejlsberg Nov 7, 2016
8aef1e6
Type inference for mapped types
ahejlsberg Nov 8, 2016
7ca5923
Merge branch 'master' into mappedTypes
ahejlsberg Nov 8, 2016
a562d6e
Make keyof T assignable to and subtype of string | number
ahejlsberg Nov 8, 2016
cf2953b
Add relations for keyof S / keyof T and [P in S]: X / [P in T]: X
ahejlsberg Nov 8, 2016
aca7e2f
Don't include private/protected properties in keyof T
ahejlsberg Nov 8, 2016
3dd11e4
Properly implement type relationship for '[P in S]: X' and '[P in T]: Y'
ahejlsberg Nov 9, 2016
2170ff6
Defer resolution of mapped types to enable recursive definitions
ahejlsberg Nov 10, 2016
b81c226
Use pull model to obtain type alias information for type nodes
ahejlsberg Nov 10, 2016
de2da2c
Accept new baselines
ahejlsberg Nov 10, 2016
aca1ab3
Check mapped type constraint is assignable to string | number
ahejlsberg Nov 10, 2016
64d2698
Merge branch 'master' into mappedTypes
ahejlsberg Nov 10, 2016
cd185f2
Add declaration emit support
ahejlsberg Nov 10, 2016
1c7ec6b
Add missing node visits in forEachChild
ahejlsberg Nov 11, 2016
364142c
Improve type inference for types with same generic type alias
ahejlsberg Nov 11, 2016
e9b6ddc
Add tests
ahejlsberg Nov 11, 2016
ca3f797
More tests
ahejlsberg Nov 11, 2016
5028a44
Accept new baselines
ahejlsberg Nov 11, 2016
9ac7667
Address CR feedback
ahejlsberg Nov 12, 2016
6ceab7b
Accept new baselines
ahejlsberg Nov 12, 2016
cd05c07
Add comment explaining type alias instantiation strategy
ahejlsberg Nov 13, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Handle readonly and optional properties + index signatures
  • Loading branch information
ahejlsberg committed Nov 6, 2016
commit 507ab30e9c4d2ab02d4caf8d033ae11226c4753a
57 changes: 45 additions & 12 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2546,13 +2546,20 @@ namespace ts {
writePunctuation(writer, SyntaxKind.OpenBraceToken);
writer.writeLine();
writer.increaseIndent();
if (type.isReadonly) {
writeKeyword(writer, SyntaxKind.ReadonlyKeyword);
writeSpace(writer);
}
writePunctuation(writer, SyntaxKind.OpenBracketToken);
appendSymbolNameOnly((type.target || type).typeParameter.symbol, writer);
appendSymbolNameOnly(type.typeParameter.symbol, writer);
writeSpace(writer);
writeKeyword(writer, SyntaxKind.InKeyword);
writeSpace(writer);
writeType(constraintType, TypeFormatFlags.None);
writePunctuation(writer, SyntaxKind.CloseBracketToken);
if (type.isOptional) {
writePunctuation(writer, SyntaxKind.QuestionToken);
}
writePunctuation(writer, SyntaxKind.ColonToken);
writeSpace(writer);
writeType(getTemplateTypeFromMappedType(type), TypeFormatFlags.None);
Expand Down Expand Up @@ -4453,28 +4460,40 @@ namespace ts {
const members: SymbolTable = createMap<Symbol>();
let stringIndexInfo: IndexInfo;
let numberIndexInfo: IndexInfo;
const target = type.target || type;
const keyType = getApparentType(getConstraintTypeFromMappedType(type));
const constraintType = getConstraintTypeFromMappedType(type);
const keyType = constraintType.flags & TypeFlags.TypeParameter ? getApparentType(constraintType) : constraintType;
const iterationType = keyType.flags & TypeFlags.Index ? getIndexType(getApparentType((<IndexType>keyType).type)) : keyType;
forEachType(iterationType, t => {
const iterationMapper = createUnaryTypeMapper(target.typeParameter, t);
const iterationMapper = createUnaryTypeMapper(type.typeParameter, t);
const templateMapper = type.mapper ? combineTypeMappers(type.mapper, iterationMapper) : iterationMapper;
const propType = instantiateType(type.templateType, templateMapper);
if (t.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral | TypeFlags.EnumLiteral)) {
const propName = (<LiteralType>t).text;
const prop = <TransientSymbol>createSymbol(SymbolFlags.Property | SymbolFlags.Transient, propName);
prop.type = instantiateType(target.templateType, templateMapper);
const optionalFlag = type.isOptional ? SymbolFlags.Optional : 0;
const prop = <TransientSymbol>createSymbol(SymbolFlags.Property | SymbolFlags.Transient | optionalFlag, propName);
prop.type = addOptionality(propType, type.isOptional);
prop.isReadonly = type.isReadonly;
members[propName] = prop;
}
else if (t.flags & TypeFlags.String) {
stringIndexInfo = createIndexInfo(propType, type.isReadonly);
}
else if (t.flags & TypeFlags.Number) {
numberIndexInfo = createIndexInfo(propType, type.isReadonly);
}
});
if (stringIndexInfo && numberIndexInfo && isTypeIdenticalTo(stringIndexInfo.type, numberIndexInfo.type)) {
numberIndexInfo = undefined;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you put this next to mapType and filterType?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo);
}

function getConstraintTypeFromMappedType(type: MappedType) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jsdoc format

@sandersn sandersn Nov 11, 2016

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like T should be U to match the explanation below.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, it looks like U is bound to each constituent of K? The order and relationships of things in this explanation is not clear.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed the comments.

return instantiateType(getConstraintOfTypeParameter((type.target || type).typeParameter), type.mapper || identityMapper);
return instantiateType(getConstraintOfTypeParameter(type.typeParameter), type.mapper || identityMapper);
}

function getTemplateTypeFromMappedType(type: MappedType) {
return instantiateType((type.target || type).templateType, type.mapper || identityMapper);
return instantiateType(type.templateType, type.mapper || identityMapper);
}

function resolveStructuredTypeMembers(type: StructuredType): ResolvedType {
Expand Down Expand Up @@ -5919,6 +5938,8 @@ namespace ts {
const type = <MappedType>createObjectType(ObjectFlags.Mapped);
type.typeParameter = getDeclaredTypeOfTypeParameter(getSymbolOfNode(node.typeParameter));
type.templateType = node.type ? getTypeFromTypeNode(node.type) : anyType;
type.isReadonly = !!node.readonlyToken;
type.isOptional = !!node.questionToken;
type.aliasSymbol = aliasSymbol;
type.aliasTypeArguments = aliasTypeArguments;
links.resolvedType = type;
Expand Down Expand Up @@ -6265,15 +6286,27 @@ namespace ts {
return result;
}

function instantiateAnonymousOrMappedType(type: AnonymousType | MappedType, mapper: TypeMapper): ObjectType {
const result = <AnonymousType | MappedType>createObjectType(type.objectFlags | ObjectFlags.Instantiated, type.symbol);
function instantiateAnonymousType(type: AnonymousType, mapper: TypeMapper): AnonymousType {
const result = <AnonymousType>createObjectType(ObjectFlags.Anonymous | ObjectFlags.Instantiated, type.symbol);
result.target = type.objectFlags & ObjectFlags.Instantiated ? type.target : type;
result.mapper = type.objectFlags & ObjectFlags.Instantiated ? combineTypeMappers(type.mapper, mapper) : mapper;
result.aliasSymbol = type.aliasSymbol;
result.aliasTypeArguments = mapper.targetTypes;
return result;
}

function instantiateMappedType(type: MappedType, mapper: TypeMapper): MappedType {
const result = <MappedType>createObjectType(ObjectFlags.Mapped | ObjectFlags.Instantiated, type.symbol);
result.typeParameter = type.typeParameter;
result.templateType = type.templateType;
result.isReadonly = type.isReadonly;
result.isOptional = type.isOptional;
result.mapper = type.objectFlags & ObjectFlags.Instantiated ? combineTypeMappers(type.mapper, mapper) : mapper;
result.aliasSymbol = type.aliasSymbol;
result.aliasTypeArguments = mapper.targetTypes;
return result;
}

function isSymbolInScopeOfMappedTypeParameter(symbol: Symbol, mapper: TypeMapper) {
if (!(symbol.declarations && symbol.declarations.length)) {
return false;
Expand Down Expand Up @@ -6342,10 +6375,10 @@ namespace ts {
return type.symbol &&
type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) &&
((<ObjectType>type).objectFlags & ObjectFlags.Instantiated || isSymbolInScopeOfMappedTypeParameter(type.symbol, mapper)) ?
instantiateCached(type, mapper, instantiateAnonymousOrMappedType) : type;
instantiateCached(type, mapper, instantiateAnonymousType) : type;
}
if ((<ObjectType>type).objectFlags & ObjectFlags.Mapped) {
return instantiateCached(type, mapper, instantiateAnonymousOrMappedType);
return instantiateCached(type, mapper, instantiateMappedType);
}
if ((<ObjectType>type).objectFlags & ObjectFlags.Reference) {
return createTypeReference((<TypeReference>type).target, instantiateList((<TypeReference>type).typeArguments, mapper, instantiateType));
Expand Down
6 changes: 4 additions & 2 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -899,7 +899,7 @@ namespace ts {
indexType: TypeNode;
}

export interface MappedTypeNode extends TypeNode {
export interface MappedTypeNode extends TypeNode, Declaration {
kind: SyntaxKind.MappedType;
readonlyToken?: ReadonlyToken;
typeParameter: TypeParameterDeclaration;
Expand Down Expand Up @@ -2850,7 +2850,9 @@ namespace ts {
export interface MappedType extends ObjectType {
typeParameter: TypeParameter;
templateType: Type;
target?: MappedType; // Instantiation target
isReadonly: boolean;
isOptional: boolean;
// target?: MappedType; // Instantiation target
mapper?: TypeMapper; // Instantiation mapper
}

Expand Down