@@ -2165,6 +2165,11 @@ namespace ts {
21652165 else if (type.flags & TypeFlags.NumberLiteral) {
21662166 writer.writeStringLiteral((<LiteralType>type).text);
21672167 }
2168+ else if (type.flags & TypeFlags.KeysQuery) {
2169+ writer.writeKeyword("keysof");
2170+ writeSpace(writer);
2171+ writeType((<KeysQueryType>type).baseType, flags);
2172+ }
21682173 else {
21692174 // Should never get here
21702175 // { ... }
@@ -5145,6 +5150,41 @@ namespace ts {
51455150 return links.resolvedType;
51465151 }
51475152
5153+ function resolveKeysQueryType(type: KeysQueryType): Type {
5154+ // Get index types to include in the union if need be
5155+ let indexTypes: Type[];
5156+ const numberIndex = getIndexInfoOfType(type.baseType, IndexKind.Number);
5157+ const stringIndex = getIndexInfoOfType(type.baseType, IndexKind.String);
5158+ if (numberIndex) {
5159+ indexTypes = [numberType];
5160+ }
5161+ if (stringIndex) {
5162+ indexTypes ? indexTypes.push(stringType) : indexTypes = [stringType];
5163+ }
5164+ // Skip any essymbol members and remember to unescape the identifier before making a type from it
5165+ const memberTypes = concatenate(indexTypes, map(filter(getPropertiesOfType(type.baseType),
5166+ symbol => !startsWith(symbol.name, "__@")),
5167+ symbol => getLiteralTypeForText(TypeFlags.StringLiteral, unescapeIdentifier(symbol.name))
5168+ ));
5169+ return memberTypes ? getUnionType(memberTypes) : neverType;
5170+ }
5171+
5172+ function getKeysQueryType(type: Type): KeysQueryType {
5173+ const queryType = createType(TypeFlags.KeysQuery) as KeysQueryType;
5174+ queryType.baseType = type;
5175+ return queryType;
5176+ }
5177+
5178+ function getTypeFromKeysQueryNode(node: KeysQueryNode): Type {
5179+ const links = getNodeLinks(node);
5180+ if (!links.resolvedType) {
5181+ // The expression is processed as an identifier expression, which we
5182+ // then store the resulting type of into a "KeysQuery" type
5183+ links.resolvedType = getKeysQueryType(getTypeFromTypeNode(node.type));
5184+ }
5185+ return links.resolvedType;
5186+ }
5187+
51485188 function getTypeOfGlobalSymbol(symbol: Symbol, arity: number): ObjectType {
51495189
51505190 function getTypeDeclaration(symbol: Symbol): Declaration {
@@ -5569,6 +5609,8 @@ namespace ts {
55695609 return getTypeFromTypeReference(<ExpressionWithTypeArguments>node);
55705610 case SyntaxKind.TypeQuery:
55715611 return getTypeFromTypeQueryNode(<TypeQueryNode>node);
5612+ case SyntaxKind.KeysQuery:
5613+ return getTypeFromKeysQueryNode(<KeysQueryNode>node);
55725614 case SyntaxKind.ArrayType:
55735615 case SyntaxKind.JSDocArrayType:
55745616 return getTypeFromArrayTypeNode(<ArrayTypeNode>node);
@@ -5855,6 +5897,9 @@ namespace ts {
58555897 if (type.flags & TypeFlags.Intersection) {
58565898 return getIntersectionType(instantiateList((<IntersectionType>type).types, mapper, instantiateType), type.aliasSymbol, mapper.targetTypes);
58575899 }
5900+ if (type.flags & TypeFlags.KeysQuery) {
5901+ return getKeysQueryType(instantiateType((<KeysQueryType>type).baseType, mapper));
5902+ }
58585903 }
58595904 return type;
58605905 }
@@ -6185,6 +6230,8 @@ namespace ts {
61856230 if (source.flags & (TypeFlags.Number | TypeFlags.NumberLiteral) && target.flags & TypeFlags.Enum) return true;
61866231 if (source.flags & TypeFlags.NumberLiteral && target.flags & TypeFlags.EnumLiteral && (<LiteralType>source).text === (<LiteralType>target).text) return true;
61876232 }
6233+ if (source.flags & TypeFlags.KeysQuery) return isTypeRelatedTo(resolveKeysQueryType(source as KeysQueryType), target, relation);
6234+ if (target.flags & TypeFlags.KeysQuery) return isTypeRelatedTo(source, resolveKeysQueryType(target as KeysQueryType), relation);
61886235 return false;
61896236 }
61906237
@@ -13356,6 +13403,9 @@ namespace ts {
1335613403 }
1335713404 contextualType = apparentType;
1335813405 }
13406+ if (contextualType.flags & TypeFlags.KeysQuery) {
13407+ return true;
13408+ }
1335913409 if (type.flags & TypeFlags.String) {
1336013410 return maybeTypeOfKind(contextualType, TypeFlags.StringLiteral);
1336113411 }
0 commit comments