Support 'this: readonly' parameter declarations

This commit is contained in:
Anders Hejlsberg
2017-07-15 18:22:04 -10:00
parent be269dbe33
commit 73b1c7167a
3 changed files with 51 additions and 13 deletions

View File

@@ -256,6 +256,7 @@ namespace ts {
const neverType = createIntrinsicType(TypeFlags.Never, "never");
const silentNeverType = createIntrinsicType(TypeFlags.Never, "never");
const nonPrimitiveType = createIntrinsicType(TypeFlags.NonPrimitive, "object");
const readonlyThisType = createIntrinsicType(TypeFlags.Any, "readonly");
const emptyObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
@@ -5675,11 +5676,37 @@ namespace ts {
return indexInfo && !indexInfo.isReadonly ? createIndexInfo(indexInfo.type, true, indexInfo.declaration) : indexInfo;
}
function hasReadonlyThisParameter(sig: SignatureDeclaration) {
const param = firstOrUndefined(sig.parameters);
return param &&
param.name.kind === SyntaxKind.Identifier &&
(<Identifier>param.name).text === "this" &&
param.type &&
param.type.kind === SyntaxKind.ReadonlyKeyword;
}
function isReadonlyThisMethod(symbol: Symbol) {
if (symbol.flags & SymbolFlags.Method) {
for (let i = 0; i < symbol.declarations.length; i++) {
const node = symbol.declarations[i];
if (node.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.MethodSignature) {
if (!hasReadonlyThisParameter(<SignatureDeclaration>node)) {
return false;
}
}
}
return true;
}
return false;
}
function resolveReadonlyTypeMembers(type: ReadonlyObjectType) {
const target = type.type;
const members = createMap<Symbol>() as SymbolTable;
for (const symbol of getPropertiesOfObjectType(target)) {
members.set(symbol.name, instantiateSymbol(symbol, identityMapper, /*readonly*/ true));
if (!(symbol.flags & SymbolFlags.Method) || isReadonlyThisMethod(symbol)) {
members.set(symbol.name, instantiateSymbol(symbol, identityMapper, /*readonly*/ true));
}
}
const callSignatures = getSignaturesOfType(target, SignatureKind.Call);
const constructSignatures = getSignaturesOfType(target, SignatureKind.Construct);
@@ -6546,7 +6573,8 @@ namespace ts {
function getThisTypeOfSignature(signature: Signature): Type | undefined {
if (signature.thisParameter) {
return getTypeOfSymbol(signature.thisParameter);
const type = getTypeOfSymbol(signature.thisParameter);
return type !== readonlyThisType ? type : undefined;
}
}
@@ -7924,6 +7952,8 @@ namespace ts {
return neverType;
case SyntaxKind.ObjectKeyword:
return nonPrimitiveType;
case SyntaxKind.ReadonlyKeyword:
return readonlyThisType;
case SyntaxKind.ThisType:
case SyntaxKind.ThisKeyword:
return getTypeFromThisTypeNode(node);
@@ -12410,9 +12440,9 @@ namespace ts {
}
if (isClassLike(container.parent)) {
const symbol = getSymbolOfNode(container.parent);
const type = hasModifier(container, ModifierFlags.Static) ? getTypeOfSymbol(symbol) : (<InterfaceType>getDeclaredTypeOfSymbol(symbol)).thisType;
return getFlowTypeOfReference(node, type);
const classSymbol = getSymbolOfNode(container.parent);
const type = hasModifier(container, ModifierFlags.Static) ? getTypeOfSymbol(classSymbol) : (<InterfaceType>getDeclaredTypeOfSymbol(classSymbol)).thisType;
return getFlowTypeOfReference(node, isReadonlyThisMethod(getSymbolOfNode(container)) ? getReadonlyType(type) : type);
}
if (isInJavaScriptFile(node)) {

View File

@@ -2153,8 +2153,16 @@ namespace ts {
}
}
function parseParameterType(): TypeNode {
function nextTokenIsStartOfType() {
nextToken();
return isStartOfType();
}
function parseParameterType(allowReadonly: boolean): TypeNode {
if (parseOptional(SyntaxKind.ColonToken)) {
if (allowReadonly && token() === SyntaxKind.ReadonlyKeyword && !lookAhead(nextTokenIsStartOfType)) {
return parseTokenNode<TypeNode>();
}
return parseType();
}
@@ -2169,7 +2177,7 @@ namespace ts {
const node = <ParameterDeclaration>createNode(SyntaxKind.Parameter);
if (token() === SyntaxKind.ThisKeyword) {
node.name = createIdentifier(/*isIdentifier*/ true);
node.type = parseParameterType();
node.type = parseParameterType(/*allowReadonly*/ true);
return finishNode(node);
}
@@ -2193,7 +2201,7 @@ namespace ts {
}
node.questionToken = parseOptionalToken(SyntaxKind.QuestionToken);
node.type = parseParameterType();
node.type = parseParameterType(/*allowReadonly*/ false);
node.initializer = parseBindingElementInitializer(/*inParameter*/ true);
// Do not check for initializers in an ambient context for parameters. This is not

View File

@@ -3351,11 +3351,6 @@ namespace ts {
type: ObjectType;
}
// Readonly type variable (TypeFlags.Readonly)
export interface ReadonlyTypeVariable extends Type {
type: TypeVariable;
}
export interface EvolvingArrayType extends ObjectType {
elementType: Type; // Element expressions of evolving array type
finalArrayType?: Type; // Final array type of evolving array type
@@ -3425,6 +3420,11 @@ namespace ts {
constraint?: Type;
}
// Readonly type variable (TypeFlags.Readonly)
export interface ReadonlyTypeVariable extends TypeVariable {
type: TypeVariable;
}
// keyof T types (TypeFlags.Index)
export interface IndexType extends Type {
type: TypeVariable | UnionOrIntersectionType;