mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-30 10:29:18 -05:00
node builder should strive to preserve mapped type keyofness (#23815)
This commit is contained in:
@@ -3158,7 +3158,16 @@ namespace ts {
|
||||
Debug.assert(!!(type.flags & TypeFlags.Object));
|
||||
const readonlyToken = type.declaration.readonlyToken ? <ReadonlyToken | PlusToken | MinusToken>createToken(type.declaration.readonlyToken.kind) : undefined;
|
||||
const questionToken = type.declaration.questionToken ? <QuestionToken | PlusToken | MinusToken>createToken(type.declaration.questionToken.kind) : undefined;
|
||||
const typeParameterNode = typeParameterToDeclaration(getTypeParameterFromMappedType(type), context, getConstraintTypeFromMappedType(type));
|
||||
let appropriateConstraintTypeNode: TypeNode;
|
||||
if (isMappedTypeWithKeyofConstraintDeclaration(type)) {
|
||||
// We have a { [P in keyof T]: X }
|
||||
// We do this to ensure we retain the toplevel keyof-ness of the type which may be lost due to keyof distribution during `getConstraintTypeFromMappedType`
|
||||
appropriateConstraintTypeNode = createTypeOperatorNode(typeToTypeNodeHelper(getModifiersTypeFromMappedType(type), context));
|
||||
}
|
||||
else {
|
||||
appropriateConstraintTypeNode = typeToTypeNodeHelper(getConstraintTypeFromMappedType(type), context);
|
||||
}
|
||||
const typeParameterNode = typeParameterToDeclarationWithConstraint(getTypeParameterFromMappedType(type), context, appropriateConstraintTypeNode);
|
||||
const templateTypeNode = typeToTypeNodeHelper(getTemplateTypeFromMappedType(type), context);
|
||||
const mappedTypeNode = createMappedTypeNode(readonlyToken, typeParameterNode, questionToken, templateTypeNode);
|
||||
return setEmitFlags(mappedTypeNode, EmitFlags.SingleLine);
|
||||
@@ -3534,17 +3543,21 @@ namespace ts {
|
||||
return createSignatureDeclaration(kind, typeParameters, parameters, returnTypeNode, typeArguments);
|
||||
}
|
||||
|
||||
function typeParameterToDeclaration(type: TypeParameter, context: NodeBuilderContext, constraint = getConstraintFromTypeParameter(type)): TypeParameterDeclaration {
|
||||
function typeParameterToDeclarationWithConstraint(type: TypeParameter, context: NodeBuilderContext, constraintNode: TypeNode): TypeParameterDeclaration {
|
||||
const savedContextFlags = context.flags;
|
||||
context.flags &= ~NodeBuilderFlags.WriteTypeParametersInQualifiedName; // Avoids potential infinite loop when building for a claimspace with a generic
|
||||
const name = symbolToName(type.symbol, context, SymbolFlags.Type, /*expectsIdentifier*/ true);
|
||||
const constraintNode = constraint && typeToTypeNodeHelper(constraint, context);
|
||||
const defaultParameter = getDefaultFromTypeParameter(type);
|
||||
const defaultParameterNode = defaultParameter && typeToTypeNodeHelper(defaultParameter, context);
|
||||
context.flags = savedContextFlags;
|
||||
return createTypeParameterDeclaration(name, constraintNode, defaultParameterNode);
|
||||
}
|
||||
|
||||
function typeParameterToDeclaration(type: TypeParameter, context: NodeBuilderContext, constraint = getConstraintFromTypeParameter(type)): TypeParameterDeclaration {
|
||||
const constraintNode = constraint && typeToTypeNodeHelper(constraint, context);
|
||||
return typeParameterToDeclarationWithConstraint(type, context, constraintNode);
|
||||
}
|
||||
|
||||
function symbolToParameterDeclaration(parameterSymbol: Symbol, context: NodeBuilderContext, preserveModifierFlags?: boolean): ParameterDeclaration {
|
||||
const parameterDeclaration = getDeclarationOfKind<ParameterDeclaration>(parameterSymbol, SyntaxKind.Parameter);
|
||||
Debug.assert(!!parameterDeclaration || isTransientSymbol(parameterSymbol) && !!parameterSymbol.isRestParameter);
|
||||
@@ -6221,10 +6234,8 @@ namespace ts {
|
||||
const templateType = getTemplateTypeFromMappedType(<MappedType>type.target || type);
|
||||
const modifiersType = getApparentType(getModifiersTypeFromMappedType(type)); // The 'T' in 'keyof T'
|
||||
const templateModifiers = getMappedTypeModifiers(type);
|
||||
const constraintDeclaration = type.declaration.typeParameter.constraint;
|
||||
const include = keyofStringsOnly ? TypeFlags.StringLiteral : TypeFlags.StringOrNumberLiteralOrUnique;
|
||||
if (constraintDeclaration.kind === SyntaxKind.TypeOperator &&
|
||||
(<TypeOperatorNode>constraintDeclaration).operator === SyntaxKind.KeyOfKeyword) {
|
||||
if (isMappedTypeWithKeyofConstraintDeclaration(type)) {
|
||||
// We have a { [P in keyof T]: X }
|
||||
for (const prop of getPropertiesOfType(modifiersType)) {
|
||||
addMemberForKeyType(getLiteralTypeFromPropertyName(prop, include), /*_index*/ undefined, prop);
|
||||
@@ -6301,15 +6312,23 @@ namespace ts {
|
||||
unknownType);
|
||||
}
|
||||
|
||||
function getConstraintDeclarationForMappedType(type: MappedType) {
|
||||
return type.declaration.typeParameter.constraint;
|
||||
}
|
||||
|
||||
function isMappedTypeWithKeyofConstraintDeclaration(type: MappedType) {
|
||||
const constraintDeclaration = getConstraintDeclarationForMappedType(type);
|
||||
return constraintDeclaration.kind === SyntaxKind.TypeOperator &&
|
||||
(<TypeOperatorNode>constraintDeclaration).operator === SyntaxKind.KeyOfKeyword;
|
||||
}
|
||||
|
||||
function getModifiersTypeFromMappedType(type: MappedType) {
|
||||
if (!type.modifiersType) {
|
||||
const constraintDeclaration = type.declaration.typeParameter.constraint;
|
||||
if (constraintDeclaration.kind === SyntaxKind.TypeOperator &&
|
||||
(<TypeOperatorNode>constraintDeclaration).operator === SyntaxKind.KeyOfKeyword) {
|
||||
if (isMappedTypeWithKeyofConstraintDeclaration(type)) {
|
||||
// If the constraint declaration is a 'keyof T' node, the modifiers type is T. We check
|
||||
// AST nodes here because, when T is a non-generic type, the logic below eagerly resolves
|
||||
// 'keyof T' to a literal union type and we can't recover T from that type.
|
||||
type.modifiersType = instantiateType(getTypeFromTypeNode((<TypeOperatorNode>constraintDeclaration).type), type.mapper || identityMapper);
|
||||
type.modifiersType = instantiateType(getTypeFromTypeNode((<TypeOperatorNode>getConstraintDeclarationForMappedType(type)).type), type.mapper || identityMapper);
|
||||
}
|
||||
else {
|
||||
// Otherwise, get the declared constraint type, and if the constraint type is a type parameter,
|
||||
|
||||
Reference in New Issue
Block a user