import { AccessorDeclaration, AllAccessorDeclarations, ArrayLiteralExpression, ArrowFunction, AsExpression, BinaryExpression, ClassExpression, CompilerOptions, ConditionalTypeNode, countWhere, Debug, Declaration, ElementAccessExpression, EmitFlags, Expression, factory, findAncestor, forEachReturnStatement, FunctionExpression, FunctionFlags, FunctionLikeDeclaration, GetAccessorDeclaration, getEffectiveReturnTypeNode, getEffectiveSetAccessorTypeAnnotationNode, getEffectiveTypeAnnotationNode, getEmitFlags, getEmitScriptTarget, getFunctionFlags, getJSDocType, getJSDocTypeAssertionType, getSourceFileOfNode, getStrictOptionValue, hasDynamicName, HasInferredType, Identifier, ImportTypeNode, IndexedAccessTypeNode, IntersectionTypeNode, IntroducesNewScopeNode, isAsExpression, isBlock, isCallExpression, isComputedPropertyName, isConditionalTypeNode, isConstTypeReference, isDeclaration, isDeclarationReadonly, isEntityName, isEntityNameExpression, isExpressionWithTypeArguments, isFunctionLike, isFunctionLikeDeclaration, isGetAccessor, isIdentifier, isIdentifierText, isImportAttributes, isImportTypeNode, isIndexedAccessTypeNode, isInJSFile, isJSDocAllType, isJSDocConstructSignature, isJSDocFunctionType, isJSDocIndexSignature, isJSDocNonNullableType, isJSDocNullableType, isJSDocOptionalType, isJSDocTypeAssertion, isJSDocTypeExpression, isJSDocTypeLiteral, isJSDocUnknownType, isJSDocVariadicType, isJsxElement, isJsxExpression, isKeyword, isLiteralImportTypeNode, isLiteralTypeNode, isMappedTypeNode, isModifier, isNamedDeclaration, isNewScopeNode, isOptionalDeclaration, isParameter, isPrimitiveLiteralValue, isPropertyDeclaration, isPropertySignature, isShorthandPropertyAssignment, isSpreadAssignment, isStringLiteral, isThisTypeNode, isTupleTypeNode, isTypeAssertionExpression, isTypeLiteralNode, isTypeNode, isTypeOperatorNode, isTypeParameterDeclaration, isTypePredicateNode, isTypeQueryNode, isTypeReferenceNode, isUnionTypeNode, isValueSignatureDeclaration, isVarConstLike, isVariableDeclaration, JSDocParameterTag, JSDocPropertyTag, JSDocSignature, JsxAttributeValue, KeywordTypeSyntaxKind, map, mapDefined, MethodDeclaration, Mutable, Node, NodeArray, NodeBuilderFlags, NodeFlags, nodeIsMissing, NoSubstitutionTemplateLiteral, ObjectLiteralExpression, ParameterDeclaration, ParenthesizedExpression, ParenthesizedTypeNode, PrefixUnaryExpression, PrimitiveLiteral, PropertyAccessExpression, PropertyAssignment, PropertyDeclaration, PropertyName, PropertySignature, SetAccessorDeclaration, setCommentRange, setEmitFlags, setOriginalNode, setTextRangePosEnd, ShorthandPropertyAssignment, SignatureDeclaration, skipTypeParentheses, StringLiteral, Symbol, SyntacticNodeBuilder, SyntacticTypeNodeBuilderContext, SyntacticTypeNodeBuilderResolver, SyntaxKind, TypeAssertion, TypeElement, TypeNode, TypeOperatorNode, TypeParameterDeclaration, TypeQueryNode, TypeReferenceNode, UnionTypeNode, VariableDeclaration, visitEachChild as visitEachChildWorker, visitNode, visitNodes, Visitor, walkUpParenthesizedExpressions, } from "./_namespaces/ts.js"; type SyntacticResult = | { type: TypeNode; reportFallback: undefined; } | { type: undefined; reportFallback: true; } | { type: undefined; reportFallback: false; }; function syntacticResult(type: TypeNode | undefined): SyntacticResult; function syntacticResult(type: undefined, reportFallback: boolean): SyntacticResult; function syntacticResult(type: TypeNode | undefined, reportFallback: boolean = true) { return { type, reportFallback } as SyntacticResult; } const notImplemented: SyntacticResult = syntacticResult(/*type*/ undefined, /*reportFallback*/ false); const alreadyReported: SyntacticResult = syntacticResult(/*type*/ undefined, /*reportFallback*/ false); const failed: SyntacticResult = syntacticResult(/*type*/ undefined, /*reportFallback*/ true); /** @internal */ export function createSyntacticTypeNodeBuilder( options: CompilerOptions, resolver: SyntacticTypeNodeBuilderResolver, ): SyntacticNodeBuilder { const strictNullChecks = getStrictOptionValue(options, "strictNullChecks"); return { serializeTypeOfDeclaration, serializeReturnTypeForSignature, serializeTypeOfExpression, serializeTypeOfAccessor, tryReuseExistingTypeNode(context: SyntacticTypeNodeBuilderContext, existing: TypeNode): TypeNode | undefined { if (!resolver.canReuseTypeNode(context, existing)) { return undefined; } return tryReuseExistingTypeNode(context, existing); }, }; function reuseNode(context: SyntacticTypeNodeBuilderContext, node: T, range?: Node): T; function reuseNode(context: SyntacticTypeNodeBuilderContext, node: T | undefined, range?: Node): T | undefined; function reuseNode(context: SyntacticTypeNodeBuilderContext, node: T | undefined, range: Node | undefined = node) { return node === undefined ? undefined : resolver.markNodeReuse(context, node.flags & NodeFlags.Synthesized ? node : factory.cloneNode(node), range ?? node); } function tryReuseExistingTypeNode(context: SyntacticTypeNodeBuilderContext, existing: TypeNode): TypeNode | undefined { const { finalizeBoundary, startRecoveryScope, hadError, markError } = resolver.createRecoveryBoundary(context); const transformed = visitNode(existing, visitExistingNodeTreeSymbols, isTypeNode); if (!finalizeBoundary()) { return undefined; } context.approximateLength += existing.end - existing.pos; return transformed; function visitExistingNodeTreeSymbols(node: Node): Node | undefined { // If there was an error in a sibling node bail early, the result will be discarded anyway if (hadError()) return node; const recover = startRecoveryScope(); const onExitNewScope = isNewScopeNode(node) ? resolver.enterNewScope(context, node) : undefined; const result = visitExistingNodeTreeSymbolsWorker(node); onExitNewScope?.(); // If there was an error, maybe we can recover by serializing the actual type of the node if (hadError()) { if (isTypeNode(node) && !isTypePredicateNode(node)) { recover(); return resolver.serializeExistingTypeNode(context, node); } return node; } // We want to clone the subtree, so when we mark it up with __pos and __end in quickfixes, // we don't get odd behavior because of reused nodes. We also need to clone to _remove_ // the position information if the node comes from a different file than the one the node builder // is set to build for (even though we are reusing the node structure, the position information // would make the printer print invalid spans for literals and identifiers, and the formatter would // choke on the mismatched positonal spans between a parent and an injected child from another file). return result ? resolver.markNodeReuse(context, result, node) : undefined; } function tryVisitSimpleTypeNode(node: TypeNode): TypeNode | undefined { const innerNode = skipTypeParentheses(node); switch (innerNode.kind) { case SyntaxKind.TypeReference: return tryVisitTypeReference(innerNode as TypeReferenceNode); case SyntaxKind.TypeQuery: return tryVisitTypeQuery(innerNode as TypeQueryNode); case SyntaxKind.IndexedAccessType: return tryVisitIndexedAccess(innerNode as IndexedAccessTypeNode); case SyntaxKind.TypeOperator: const typeOperatorNode = innerNode as TypeOperatorNode; if (typeOperatorNode.operator === SyntaxKind.KeyOfKeyword) { return tryVisitKeyOf(typeOperatorNode); } } return visitNode(node, visitExistingNodeTreeSymbols, isTypeNode); } function tryVisitIndexedAccess(node: IndexedAccessTypeNode): TypeNode | undefined { const resultObjectType = tryVisitSimpleTypeNode(node.objectType); if (resultObjectType === undefined) { return undefined; } return factory.updateIndexedAccessTypeNode(node, resultObjectType, visitNode(node.indexType, visitExistingNodeTreeSymbols, isTypeNode)!); } function tryVisitKeyOf(node: TypeOperatorNode): TypeNode | undefined { Debug.assertEqual(node.operator, SyntaxKind.KeyOfKeyword); const type = tryVisitSimpleTypeNode(node.type); if (type === undefined) { return undefined; } return factory.updateTypeOperatorNode(node, type); } function tryVisitTypeQuery(node: TypeQueryNode): TypeNode | undefined { const { introducesError, node: exprName } = resolver.trackExistingEntityName(context, node.exprName); if (!introducesError) { return factory.updateTypeQueryNode( node, exprName, visitNodes(node.typeArguments, visitExistingNodeTreeSymbols, isTypeNode), ); } const serializedName = resolver.serializeTypeName(context, node.exprName, /*isTypeOf*/ true); if (serializedName) { return resolver.markNodeReuse(context, serializedName, node.exprName); } } function tryVisitTypeReference(node: TypeReferenceNode): TypeNode | undefined { if (resolver.canReuseTypeNode(context, node)) { const { introducesError, node: newName } = resolver.trackExistingEntityName(context, node.typeName); const typeArguments = visitNodes(node.typeArguments, visitExistingNodeTreeSymbols, isTypeNode); if (!introducesError) { const updated = factory.updateTypeReferenceNode( node, newName, typeArguments, ); return resolver.markNodeReuse(context, updated, node); } else { const serializedName = resolver.serializeTypeName(context, node.typeName, /*isTypeOf*/ false, typeArguments); if (serializedName) { return resolver.markNodeReuse(context, serializedName, node.typeName); } } } } function visitExistingNodeTreeSymbolsWorker(node: Node): Node | undefined { if (isJSDocTypeExpression(node)) { // Unwrap JSDocTypeExpressions return visitNode(node.type, visitExistingNodeTreeSymbols, isTypeNode); } // We don't _actually_ support jsdoc namepath types, emit `any` instead if (isJSDocAllType(node) || node.kind === SyntaxKind.JSDocNamepathType) { return factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); } if (isJSDocUnknownType(node)) { return factory.createKeywordTypeNode(SyntaxKind.UnknownKeyword); } if (isJSDocNullableType(node)) { return factory.createUnionTypeNode([visitNode(node.type, visitExistingNodeTreeSymbols, isTypeNode)!, factory.createLiteralTypeNode(factory.createNull())]); } if (isJSDocOptionalType(node)) { return factory.createUnionTypeNode([visitNode(node.type, visitExistingNodeTreeSymbols, isTypeNode)!, factory.createKeywordTypeNode(SyntaxKind.UndefinedKeyword)]); } if (isJSDocNonNullableType(node)) { return visitNode(node.type, visitExistingNodeTreeSymbols); } if (isJSDocVariadicType(node)) { return factory.createArrayTypeNode(visitNode(node.type, visitExistingNodeTreeSymbols, isTypeNode)!); } if (isJSDocTypeLiteral(node)) { return factory.createTypeLiteralNode(map(node.jsDocPropertyTags, t => { const name = visitNode(isIdentifier(t.name) ? t.name : t.name.right, visitExistingNodeTreeSymbols, isIdentifier)!; const overrideTypeNode = resolver.getJsDocPropertyOverride(context, node, t); return factory.createPropertySignature( /*modifiers*/ undefined, name, t.isBracketed || t.typeExpression && isJSDocOptionalType(t.typeExpression.type) ? factory.createToken(SyntaxKind.QuestionToken) : undefined, overrideTypeNode || (t.typeExpression && visitNode(t.typeExpression.type, visitExistingNodeTreeSymbols, isTypeNode)) || factory.createKeywordTypeNode(SyntaxKind.AnyKeyword), ); })); } if (isTypeReferenceNode(node) && isIdentifier(node.typeName) && node.typeName.escapedText === "") { return setOriginalNode(factory.createKeywordTypeNode(SyntaxKind.AnyKeyword), node); } if ((isExpressionWithTypeArguments(node) || isTypeReferenceNode(node)) && isJSDocIndexSignature(node)) { return factory.createTypeLiteralNode([factory.createIndexSignature( /*modifiers*/ undefined, [factory.createParameterDeclaration( /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, "x", /*questionToken*/ undefined, visitNode(node.typeArguments![0], visitExistingNodeTreeSymbols, isTypeNode), )], visitNode(node.typeArguments![1], visitExistingNodeTreeSymbols, isTypeNode), )]); } if (isJSDocFunctionType(node)) { if (isJSDocConstructSignature(node)) { let newTypeNode: TypeNode | undefined; return factory.createConstructorTypeNode( /*modifiers*/ undefined, visitNodes(node.typeParameters, visitExistingNodeTreeSymbols, isTypeParameterDeclaration), mapDefined(node.parameters, (p, i) => p.name && isIdentifier(p.name) && p.name.escapedText === "new" ? (newTypeNode = p.type, undefined) : factory.createParameterDeclaration( /*modifiers*/ undefined, getEffectiveDotDotDotForParameter(p), resolver.markNodeReuse(context, factory.createIdentifier(getNameForJSDocFunctionParameter(p, i)), p), factory.cloneNode(p.questionToken), visitNode(p.type, visitExistingNodeTreeSymbols, isTypeNode), /*initializer*/ undefined, )), visitNode(newTypeNode || node.type, visitExistingNodeTreeSymbols, isTypeNode) || factory.createKeywordTypeNode(SyntaxKind.AnyKeyword), ); } else { return factory.createFunctionTypeNode( visitNodes(node.typeParameters, visitExistingNodeTreeSymbols, isTypeParameterDeclaration), map(node.parameters, (p, i) => factory.createParameterDeclaration( /*modifiers*/ undefined, getEffectiveDotDotDotForParameter(p), resolver.markNodeReuse(context, factory.createIdentifier(getNameForJSDocFunctionParameter(p, i)), p), factory.cloneNode(p.questionToken), visitNode(p.type, visitExistingNodeTreeSymbols, isTypeNode), /*initializer*/ undefined, )), visitNode(node.type, visitExistingNodeTreeSymbols, isTypeNode) || factory.createKeywordTypeNode(SyntaxKind.AnyKeyword), ); } } if (isThisTypeNode(node)) { if (resolver.canReuseTypeNode(context, node)) { return node; } markError(); return node; } if (isTypeParameterDeclaration(node)) { const { node: newName } = resolver.trackExistingEntityName(context, node.name); return factory.updateTypeParameterDeclaration( node, visitNodes(node.modifiers, visitExistingNodeTreeSymbols, isModifier), // resolver.markNodeReuse(context, typeParameterToName(getDeclaredTypeOfSymbol(getSymbolOfDeclaration(node)), context), node), newName, visitNode(node.constraint, visitExistingNodeTreeSymbols, isTypeNode), visitNode(node.default, visitExistingNodeTreeSymbols, isTypeNode), ); } if (isIndexedAccessTypeNode(node)) { const result = tryVisitIndexedAccess(node); if (!result) { markError(); return node; } return result; } if (isTypeReferenceNode(node)) { const result = tryVisitTypeReference(node); if (result) { return result; } markError(); return node; } if (isLiteralImportTypeNode(node)) { // assert keyword in imported attributes is deprecated, so we don't reuse types that contain it // Ex: import("pkg", { assert: {} } if (node.attributes?.token === SyntaxKind.AssertKeyword) { markError(); return node; } if (!resolver.canReuseTypeNode(context, node)) { return resolver.serializeExistingTypeNode(context, node); } const specifier = rewriteModuleSpecifier(node, node.argument.literal); const literal = specifier === node.argument.literal ? reuseNode(context, node.argument.literal) : specifier; return factory.updateImportTypeNode( node, literal === node.argument.literal ? reuseNode(context, node.argument) : factory.createLiteralTypeNode(literal), visitNode(node.attributes, visitExistingNodeTreeSymbols, isImportAttributes), visitNode(node.qualifier, visitExistingNodeTreeSymbols, isEntityName), visitNodes(node.typeArguments, visitExistingNodeTreeSymbols, isTypeNode), node.isTypeOf, ); } if (isNamedDeclaration(node) && node.name.kind === SyntaxKind.ComputedPropertyName && !resolver.hasLateBindableName(node)) { if (!hasDynamicName(node)) { return visitEachChild(node, visitExistingNodeTreeSymbols); } if (resolver.shouldRemoveDeclaration(context, node)) { return undefined; } } if ( (isFunctionLike(node) && !node.type) || (isPropertyDeclaration(node) && !node.type && !node.initializer) || (isPropertySignature(node) && !node.type && !node.initializer) || (isParameter(node) && !node.type && !node.initializer) ) { let visited = visitEachChild(node, visitExistingNodeTreeSymbols); if (visited === node) { visited = resolver.markNodeReuse(context, factory.cloneNode(node), node); } (visited as Mutable).type = factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); if (isParameter(node)) { (visited as Mutable).modifiers = undefined; } return visited; } if (isTypeQueryNode(node)) { const result = tryVisitTypeQuery(node); if (!result) { markError(); return node; } return result; } if (isComputedPropertyName(node) && isEntityNameExpression(node.expression)) { const { node: result, introducesError } = resolver.trackExistingEntityName(context, node.expression); if (!introducesError) { return factory.updateComputedPropertyName(node, result); } else { const computedPropertyNameType = resolver.serializeTypeOfExpression(context, node.expression); let literal; if (isLiteralTypeNode(computedPropertyNameType)) { literal = computedPropertyNameType.literal; } else { const evaluated = resolver.evaluateEntityNameExpression(node.expression); const literalNode = typeof evaluated.value === "string" ? factory.createStringLiteral(evaluated.value, /*isSingleQuote*/ undefined) : typeof evaluated.value === "number" ? factory.createNumericLiteral(evaluated.value, /*numericLiteralFlags*/ 0) : undefined; if (!literalNode) { if (isImportTypeNode(computedPropertyNameType)) { resolver.trackComputedName(context, node.expression); } return node; } literal = literalNode; } if (literal.kind === SyntaxKind.StringLiteral && isIdentifierText(literal.text, getEmitScriptTarget(options))) { return factory.createIdentifier(literal.text); } if (literal.kind === SyntaxKind.NumericLiteral && !literal.text.startsWith("-")) { return literal; } return factory.updateComputedPropertyName(node, literal); } } if (isTypePredicateNode(node)) { let parameterName; if (isIdentifier(node.parameterName)) { const { node: result, introducesError } = resolver.trackExistingEntityName(context, node.parameterName); // Should not usually happen the only case is when a type predicate comes from a JSDoc type annotation with it's own parameter symbol definition. // /** @type {(v: unknown) => v is undefined} */ // const isUndef = v => v === undefined; if (introducesError) markError(); parameterName = result; } else { parameterName = factory.cloneNode(node.parameterName); } return factory.updateTypePredicateNode(node, factory.cloneNode(node.assertsModifier), parameterName, visitNode(node.type, visitExistingNodeTreeSymbols, isTypeNode)); } if (isTupleTypeNode(node) || isTypeLiteralNode(node) || isMappedTypeNode(node)) { const visited = visitEachChild(node, visitExistingNodeTreeSymbols); const clone = resolver.markNodeReuse(context, visited === node ? factory.cloneNode(node) : visited, node); const flags = getEmitFlags(clone); setEmitFlags(clone, flags | (context.flags & NodeBuilderFlags.MultilineObjectLiterals && isTypeLiteralNode(node) ? 0 : EmitFlags.SingleLine)); return clone; } if (isStringLiteral(node) && !!(context.flags & NodeBuilderFlags.UseSingleQuotesForStringLiteralType) && !node.singleQuote) { const clone = factory.cloneNode(node); (clone as Mutable).singleQuote = true; return clone; } if (isConditionalTypeNode(node)) { const checkType = visitNode(node.checkType, visitExistingNodeTreeSymbols, isTypeNode)!; const disposeScope = resolver.enterNewScope(context, node); const extendType = visitNode(node.extendsType, visitExistingNodeTreeSymbols, isTypeNode)!; const trueType = visitNode(node.trueType, visitExistingNodeTreeSymbols, isTypeNode)!; disposeScope(); const falseType = visitNode(node.falseType, visitExistingNodeTreeSymbols, isTypeNode)!; return factory.updateConditionalTypeNode( node, checkType, extendType, trueType, falseType, ); } if (isTypeOperatorNode(node)) { if (node.operator === SyntaxKind.UniqueKeyword && node.type.kind === SyntaxKind.SymbolKeyword) { if (!resolver.canReuseTypeNode(context, node)) { markError(); return node; } } else if (node.operator === SyntaxKind.KeyOfKeyword) { const result = tryVisitKeyOf(node); if (!result) { markError(); return node; } return result; } } return visitEachChild(node, visitExistingNodeTreeSymbols); function visitEachChild(node: T, visitor: Visitor): T; function visitEachChild(node: T | undefined, visitor: Visitor): T | undefined; function visitEachChild(node: T | undefined, visitor: Visitor): T | undefined { const nonlocalNode = !context.enclosingFile || context.enclosingFile !== getSourceFileOfNode(node); return visitEachChildWorker(node, visitor, /*context*/ undefined, nonlocalNode ? visitNodesWithoutCopyingPositions : undefined); } function visitNodesWithoutCopyingPositions( nodes: NodeArray | undefined, visitor: Visitor, test?: (node: Node) => boolean, start?: number, count?: number, ): NodeArray | undefined { let result = visitNodes(nodes, visitor, test, start, count); if (result) { if (result.pos !== -1 || result.end !== -1) { if (result === nodes) { result = factory.createNodeArray(nodes.slice(), nodes.hasTrailingComma); } setTextRangePosEnd(result, -1, -1); } } return result; } function getEffectiveDotDotDotForParameter(p: ParameterDeclaration) { return p.dotDotDotToken || (p.type && isJSDocVariadicType(p.type) ? factory.createToken(SyntaxKind.DotDotDotToken) : undefined); } /** Note that `new:T` parameters are not handled, but should be before calling this function. */ function getNameForJSDocFunctionParameter(p: ParameterDeclaration, index: number) { return p.name && isIdentifier(p.name) && p.name.escapedText === "this" ? "this" : getEffectiveDotDotDotForParameter(p) ? `args` : `arg${index}`; } function rewriteModuleSpecifier(parent: ImportTypeNode, lit: StringLiteral) { const newName = resolver.getModuleSpecifierOverride(context, parent, lit); return newName ? setOriginalNode(factory.createStringLiteral(newName), lit) : lit; } } } function serializeExistingTypeNode(typeNode: TypeNode, context: SyntacticTypeNodeBuilderContext, addUndefined?: boolean): TypeNode; function serializeExistingTypeNode(typeNode: TypeNode | undefined, context: SyntacticTypeNodeBuilderContext, addUndefined?: boolean): TypeNode | undefined; function serializeExistingTypeNode(typeNode: TypeNode | undefined, context: SyntacticTypeNodeBuilderContext, addUndefined?: boolean): TypeNode | undefined { if (!typeNode) return undefined; let result; if ( (!addUndefined || canAddUndefined(typeNode)) && resolver.canReuseTypeNode(context, typeNode) ) { result = tryReuseExistingTypeNode(context, typeNode); if (result !== undefined) { result = addUndefinedIfNeeded(result, addUndefined, /*owner*/ undefined, context); } } return result; } function serializeTypeAnnotationOfDeclaration(declaredType: TypeNode | undefined, context: SyntacticTypeNodeBuilderContext, node: Declaration, symbol: Symbol | undefined, requiresAddingUndefined?: boolean, useFallback = requiresAddingUndefined !== undefined) { if (!declaredType) return undefined; if (!resolver.canReuseTypeNodeAnnotation(context, node, declaredType, symbol, requiresAddingUndefined)) { // If we need to add undefined, can add undefined, and the resolver says we can reuse the type, we reuse the type // If we don't know syntactically that we can add the undefined, we will report the fallback below. if (!requiresAddingUndefined || !resolver.canReuseTypeNodeAnnotation(context, node, declaredType, symbol, /*requiresAddingUndefined*/ false)) { return undefined; } } let result; if (!requiresAddingUndefined || canAddUndefined(declaredType)) { result = serializeExistingTypeNode(declaredType, context, requiresAddingUndefined); } if (result !== undefined || !useFallback) { return result; } context.tracker.reportInferenceFallback(node); return resolver.serializeExistingTypeNode(context, declaredType, requiresAddingUndefined) ?? factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); } function serializeExistingTypeNodeWithFallback(typeNode: TypeNode | undefined, context: SyntacticTypeNodeBuilderContext, addUndefined?: boolean, targetNode?: Node) { if (!typeNode) return undefined; const result = serializeExistingTypeNode(typeNode, context, addUndefined); if (result !== undefined) { return result; } context.tracker.reportInferenceFallback(targetNode ?? typeNode); return resolver.serializeExistingTypeNode(context, typeNode, addUndefined) ?? factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); } function serializeTypeOfAccessor(accessor: AccessorDeclaration, symbol: Symbol | undefined, context: SyntacticTypeNodeBuilderContext) { return typeFromAccessor(accessor, symbol, context) ?? inferAccessorType(accessor, resolver.getAllAccessorDeclarations(accessor), context, symbol); } function serializeTypeOfExpression(expr: Expression, context: SyntacticTypeNodeBuilderContext, addUndefined?: boolean, preserveLiterals?: boolean) { const result = typeFromExpression(expr, context, /*isConstContext*/ false, addUndefined, preserveLiterals); return result.type !== undefined ? result.type : inferExpressionType(expr, context, result.reportFallback); } function serializeTypeOfDeclaration(node: HasInferredType, symbol: Symbol, context: SyntacticTypeNodeBuilderContext) { switch (node.kind) { case SyntaxKind.Parameter: case SyntaxKind.JSDocParameterTag: return typeFromParameter(node, symbol, context); case SyntaxKind.VariableDeclaration: return typeFromVariable(node, symbol, context); case SyntaxKind.PropertySignature: case SyntaxKind.JSDocPropertyTag: case SyntaxKind.PropertyDeclaration: return typeFromProperty(node, symbol, context); case SyntaxKind.BindingElement: return inferTypeOfDeclaration(node, symbol, context); case SyntaxKind.ExportAssignment: return serializeTypeOfExpression(node.expression, context, /*addUndefined*/ undefined, /*preserveLiterals*/ true); case SyntaxKind.PropertyAccessExpression: case SyntaxKind.ElementAccessExpression: case SyntaxKind.BinaryExpression: return typeFromExpandoProperty(node, symbol, context); case SyntaxKind.PropertyAssignment: case SyntaxKind.ShorthandPropertyAssignment: return typeFromPropertyAssignment(node, symbol, context); default: Debug.assertNever(node, `Node needs to be an inferrable node, found ${Debug.formatSyntaxKind((node as Node).kind)}`); } } function typeFromPropertyAssignment(node: PropertyAssignment | ShorthandPropertyAssignment, symbol: Symbol, context: SyntacticTypeNodeBuilderContext) { const typeAnnotation = getEffectiveTypeAnnotationNode(node); let result; if (typeAnnotation && resolver.canReuseTypeNodeAnnotation(context, node, typeAnnotation, symbol)) { result = serializeExistingTypeNode(typeAnnotation, context); } if (!result && node.kind === SyntaxKind.PropertyAssignment) { const initializer = node.initializer; const assertionNode = isJSDocTypeAssertion(initializer) ? getJSDocTypeAssertionType(initializer) : initializer.kind === SyntaxKind.AsExpression || initializer.kind === SyntaxKind.TypeAssertionExpression ? (initializer as AsExpression | TypeAssertion).type : undefined; if (assertionNode && !isConstTypeReference(assertionNode) && resolver.canReuseTypeNodeAnnotation(context, node, assertionNode, symbol)) { result = serializeExistingTypeNode(assertionNode, context); } } return result ?? inferTypeOfDeclaration(node, symbol, context, /*reportFallback*/ false); } function serializeReturnTypeForSignature(node: SignatureDeclaration | JSDocSignature, symbol: Symbol, context: SyntacticTypeNodeBuilderContext) { switch (node.kind) { case SyntaxKind.GetAccessor: return serializeTypeOfAccessor(node, symbol, context); case SyntaxKind.MethodDeclaration: case SyntaxKind.FunctionDeclaration: case SyntaxKind.ConstructSignature: case SyntaxKind.MethodSignature: case SyntaxKind.CallSignature: case SyntaxKind.Constructor: case SyntaxKind.SetAccessor: case SyntaxKind.IndexSignature: case SyntaxKind.FunctionType: case SyntaxKind.ConstructorType: case SyntaxKind.FunctionExpression: case SyntaxKind.ArrowFunction: case SyntaxKind.JSDocFunctionType: case SyntaxKind.JSDocSignature: return createReturnFromSignature(node, symbol, context); default: Debug.assertNever(node, `Node needs to be an inferrable node, found ${Debug.formatSyntaxKind((node as Node).kind)}`); } } function getTypeAnnotationFromAccessor(accessor: AccessorDeclaration): TypeNode | undefined { if (accessor) { return accessor.kind === SyntaxKind.GetAccessor ? (isInJSFile(accessor) && getJSDocType(accessor)) || getEffectiveReturnTypeNode(accessor) : getEffectiveSetAccessorTypeAnnotationNode(accessor); } } function getTypeAnnotationFromAllAccessorDeclarations(node: AccessorDeclaration, accessors: AllAccessorDeclarations) { let accessorType = getTypeAnnotationFromAccessor(node); if (!accessorType && node !== accessors.firstAccessor) { accessorType = getTypeAnnotationFromAccessor(accessors.firstAccessor); } if (!accessorType && accessors.secondAccessor && node !== accessors.secondAccessor) { accessorType = getTypeAnnotationFromAccessor(accessors.secondAccessor); } return accessorType; } function typeFromAccessor(node: AccessorDeclaration, symbol: Symbol | undefined, context: SyntacticTypeNodeBuilderContext): TypeNode | undefined { const accessorDeclarations = resolver.getAllAccessorDeclarations(node); const accessorType = getTypeAnnotationFromAllAccessorDeclarations(node, accessorDeclarations); if (accessorType && !isTypePredicateNode(accessorType)) { return withNewScope(context, node, () => serializeTypeAnnotationOfDeclaration(accessorType, context, node, symbol) ?? inferTypeOfDeclaration(node, symbol, context)); } if (accessorDeclarations.getAccessor) { return withNewScope(context, accessorDeclarations.getAccessor, () => createReturnFromSignature(accessorDeclarations.getAccessor!, symbol, context)); } return undefined; } function typeFromVariable(node: VariableDeclaration, symbol: Symbol, context: SyntacticTypeNodeBuilderContext): TypeNode | undefined { const declaredType = getEffectiveTypeAnnotationNode(node); let resultType = failed; if (declaredType) { resultType = syntacticResult(serializeTypeAnnotationOfDeclaration(declaredType, context, node, symbol)); } else if (node.initializer && (symbol.declarations?.length === 1 || countWhere(symbol.declarations, isVariableDeclaration) === 1)) { if (!resolver.isExpandoFunctionDeclaration(node) && !isContextuallyTyped(node)) { resultType = typeFromExpression(node.initializer, context, /*isConstContext*/ undefined, /*requiresAddingUndefined*/ undefined, isVarConstLike(node)); } } return resultType.type !== undefined ? resultType.type : inferTypeOfDeclaration(node, symbol, context, resultType.reportFallback); } function typeFromParameter(node: ParameterDeclaration | JSDocParameterTag, symbol: Symbol | undefined, context: SyntacticTypeNodeBuilderContext): TypeNode | undefined { const parent = node.parent; if (parent.kind === SyntaxKind.SetAccessor) { return serializeTypeOfAccessor(parent, /*symbol*/ undefined, context); } const declaredType = getEffectiveTypeAnnotationNode(node); const addUndefined = resolver.requiresAddingImplicitUndefined(node, symbol, context.enclosingDeclaration); let resultType = failed; if (declaredType) { resultType = syntacticResult(serializeTypeAnnotationOfDeclaration(declaredType, context, node, symbol, addUndefined)); } else if (isParameter(node) && node.initializer && isIdentifier(node.name) && !isContextuallyTyped(node)) { resultType = typeFromExpression(node.initializer, context, /*isConstContext*/ undefined, addUndefined); } return resultType.type !== undefined ? resultType.type : inferTypeOfDeclaration(node, symbol, context, resultType.reportFallback); } /** * While expando poperies are errors in TSC, in JS we try to extract the type from the binary expression; */ function typeFromExpandoProperty(node: PropertyAccessExpression | BinaryExpression | ElementAccessExpression, symbol: Symbol, context: SyntacticTypeNodeBuilderContext) { const declaredType = getEffectiveTypeAnnotationNode(node); let result; if (declaredType) { result = serializeTypeAnnotationOfDeclaration(declaredType, context, node, symbol); } const oldSuppressReportInferenceFallback = context.suppressReportInferenceFallback; context.suppressReportInferenceFallback = true; const resultType = result ?? inferTypeOfDeclaration(node, symbol, context, /*reportFallback*/ false); context.suppressReportInferenceFallback = oldSuppressReportInferenceFallback; return resultType; } function typeFromProperty(node: PropertyDeclaration | PropertySignature | JSDocPropertyTag, symbol: Symbol, context: SyntacticTypeNodeBuilderContext) { const declaredType = getEffectiveTypeAnnotationNode(node); const requiresAddingUndefined = resolver.requiresAddingImplicitUndefined(node, symbol, context.enclosingDeclaration); let resultType = failed; if (declaredType) { resultType = syntacticResult(serializeTypeAnnotationOfDeclaration(declaredType, context, node, symbol, requiresAddingUndefined)); } else { const initializer = isPropertyDeclaration(node) ? node.initializer : undefined; if (initializer && !isContextuallyTyped(node)) { const isReadonly = isDeclarationReadonly(node); resultType = typeFromExpression(initializer, context, /*isConstContext*/ undefined, requiresAddingUndefined, isReadonly); } } return resultType.type !== undefined ? resultType.type : inferTypeOfDeclaration(node, symbol, context, resultType.reportFallback); } function inferTypeOfDeclaration( node: HasInferredType | GetAccessorDeclaration | SetAccessorDeclaration, symbol: Symbol | undefined, context: SyntacticTypeNodeBuilderContext, reportFallback = true, ) { if (reportFallback) { context.tracker.reportInferenceFallback(node); } if (context.noInferenceFallback === true) { return factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); } return resolver.serializeTypeOfDeclaration(context, node, symbol); } function inferExpressionType(node: Expression, context: SyntacticTypeNodeBuilderContext, reportFallback = true, requiresAddingUndefined?: boolean) { Debug.assert(!requiresAddingUndefined); if (reportFallback) { context.tracker.reportInferenceFallback(node); } if (context.noInferenceFallback === true) { return factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); } return resolver.serializeTypeOfExpression(context, node) ?? factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); } function inferReturnTypeOfSignatureSignature(node: SignatureDeclaration | JSDocSignature, context: SyntacticTypeNodeBuilderContext, symbol: Symbol | undefined, reportFallback: boolean) { if (reportFallback) { context.tracker.reportInferenceFallback(node); } if (context.noInferenceFallback === true) { return factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); } return resolver.serializeReturnTypeForSignature(context, node, symbol) ?? factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); } function inferAccessorType(node: GetAccessorDeclaration | SetAccessorDeclaration, allAccessors: AllAccessorDeclarations, context: SyntacticTypeNodeBuilderContext, symbol: Symbol | undefined, reportFallback: boolean = true): TypeNode | undefined { if (node.kind === SyntaxKind.GetAccessor) { return createReturnFromSignature(node, symbol, context, reportFallback); } else { if (reportFallback) { context.tracker.reportInferenceFallback(node); } const result = allAccessors.getAccessor && createReturnFromSignature(allAccessors.getAccessor, symbol, context, reportFallback); return result ?? resolver.serializeTypeOfDeclaration(context, node, symbol) ?? factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); } } function withNewScope(context: SyntacticTypeNodeBuilderContext, node: IntroducesNewScopeNode | ConditionalTypeNode, fn: () => R) { const cleanup = resolver.enterNewScope(context, node); const result = fn(); cleanup(); return result; } function typeFromTypeAssertion(expression: Expression, type: TypeNode, context: SyntacticTypeNodeBuilderContext, requiresAddingUndefined: boolean): SyntacticResult { if (isConstTypeReference(type)) { return typeFromExpression(expression, context, /*isConstContext*/ true, requiresAddingUndefined); } return syntacticResult(serializeExistingTypeNodeWithFallback(type, context, requiresAddingUndefined)); } function typeFromExpression(node: Expression | JsxAttributeValue, context: SyntacticTypeNodeBuilderContext, isConstContext = false, requiresAddingUndefined = false, preserveLiterals = false): SyntacticResult { switch (node.kind) { case SyntaxKind.ParenthesizedExpression: if (isJSDocTypeAssertion(node)) { return typeFromTypeAssertion(node.expression, getJSDocTypeAssertionType(node), context, requiresAddingUndefined); } return typeFromExpression((node as ParenthesizedExpression).expression, context, isConstContext, requiresAddingUndefined); case SyntaxKind.Identifier: if (resolver.isUndefinedIdentifierExpression(node as Identifier)) { return syntacticResult(createUndefinedTypeNode()); } break; case SyntaxKind.NullKeyword: if (strictNullChecks) { return syntacticResult(addUndefinedIfNeeded(factory.createLiteralTypeNode(factory.createNull()), requiresAddingUndefined, node, context)); } else { return syntacticResult(factory.createKeywordTypeNode(SyntaxKind.AnyKeyword)); } case SyntaxKind.ArrowFunction: case SyntaxKind.FunctionExpression: Debug.type(node); return withNewScope(context, node, () => typeFromFunctionLikeExpression(node, context)); case SyntaxKind.TypeAssertionExpression: case SyntaxKind.AsExpression: const asExpression = node as AsExpression | TypeAssertion; return typeFromTypeAssertion(asExpression.expression, asExpression.type, context, requiresAddingUndefined); case SyntaxKind.PrefixUnaryExpression: const unaryExpression = node as PrefixUnaryExpression; if (isPrimitiveLiteralValue(unaryExpression)) { return typeFromPrimitiveLiteral( unaryExpression.operator === SyntaxKind.PlusToken ? unaryExpression.operand : unaryExpression, unaryExpression.operand.kind === SyntaxKind.BigIntLiteral ? SyntaxKind.BigIntKeyword : SyntaxKind.NumberKeyword, context, isConstContext || preserveLiterals, requiresAddingUndefined, ); } break; case SyntaxKind.ArrayLiteralExpression: return typeFromArrayLiteral(node as ArrayLiteralExpression, context, isConstContext, requiresAddingUndefined); case SyntaxKind.ObjectLiteralExpression: return typeFromObjectLiteral(node as ObjectLiteralExpression, context, isConstContext, requiresAddingUndefined); case SyntaxKind.ClassExpression: return syntacticResult(inferExpressionType(node as ClassExpression, context, /*reportFallback*/ true, requiresAddingUndefined)); case SyntaxKind.TemplateExpression: if (!isConstContext && !preserveLiterals) { return syntacticResult(factory.createKeywordTypeNode(SyntaxKind.StringKeyword)); } break; default: let typeKind: KeywordTypeSyntaxKind | undefined; let primitiveNode = node as PrimitiveLiteral; switch (node.kind) { case SyntaxKind.NumericLiteral: typeKind = SyntaxKind.NumberKeyword; break; case SyntaxKind.NoSubstitutionTemplateLiteral: primitiveNode = factory.createStringLiteral((node as NoSubstitutionTemplateLiteral).text); typeKind = SyntaxKind.StringKeyword; break; case SyntaxKind.StringLiteral: typeKind = SyntaxKind.StringKeyword; break; case SyntaxKind.BigIntLiteral: typeKind = SyntaxKind.BigIntKeyword; break; case SyntaxKind.TrueKeyword: case SyntaxKind.FalseKeyword: typeKind = SyntaxKind.BooleanKeyword; break; } if (typeKind) { return typeFromPrimitiveLiteral(primitiveNode, typeKind, context, isConstContext || preserveLiterals, requiresAddingUndefined); } } return failed; } function typeFromFunctionLikeExpression(fnNode: FunctionExpression | ArrowFunction, context: SyntacticTypeNodeBuilderContext) { const returnType = createReturnFromSignature(fnNode, /*symbol*/ undefined, context); const typeParameters = reuseTypeParameters(fnNode.typeParameters, context); const parameters = fnNode.parameters.map(p => ensureParameter(p, context)); return syntacticResult( factory.createFunctionTypeNode( typeParameters, parameters, returnType, ), ); } function canGetTypeFromArrayLiteral(arrayLiteral: ArrayLiteralExpression, context: SyntacticTypeNodeBuilderContext, isConstContext: boolean) { if (!isConstContext) { context.tracker.reportInferenceFallback(arrayLiteral); return false; } for (const element of arrayLiteral.elements) { if (element.kind === SyntaxKind.SpreadElement) { context.tracker.reportInferenceFallback(element); return false; } } return true; } function typeFromArrayLiteral(arrayLiteral: ArrayLiteralExpression, context: SyntacticTypeNodeBuilderContext, isConstContext: boolean, requiresAddingUndefined: boolean): SyntacticResult { if (!canGetTypeFromArrayLiteral(arrayLiteral, context, isConstContext)) { if (requiresAddingUndefined || isDeclaration(walkUpParenthesizedExpressions(arrayLiteral).parent)) { return alreadyReported; } return syntacticResult(inferExpressionType(arrayLiteral, context, /*reportFallback*/ false, requiresAddingUndefined)); } // Disable any inference fallback since we won't actually use the resulting type and we don't want to generate errors const oldNoInferenceFallback = context.noInferenceFallback; context.noInferenceFallback = true; const elementTypesInfo: TypeNode[] = []; for (const element of arrayLiteral.elements) { Debug.assert(element.kind !== SyntaxKind.SpreadElement); if (element.kind === SyntaxKind.OmittedExpression) { elementTypesInfo.push( createUndefinedTypeNode(), ); } else { const expressionType = typeFromExpression(element, context, isConstContext); const elementType = expressionType.type !== undefined ? expressionType.type : inferExpressionType(element, context, expressionType.reportFallback); elementTypesInfo.push(elementType); } } const tupleType = factory.createTupleTypeNode(elementTypesInfo); tupleType.emitNode = { flags: 1, autoGenerate: undefined, internalFlags: 0 }; context.noInferenceFallback = oldNoInferenceFallback; return notImplemented; } function canGetTypeFromObjectLiteral(objectLiteral: ObjectLiteralExpression, context: SyntacticTypeNodeBuilderContext) { let result = true; for (const prop of objectLiteral.properties) { if (prop.flags & NodeFlags.ThisNodeHasError) { result = false; break; // Bail if parse errors } if (prop.kind === SyntaxKind.ShorthandPropertyAssignment || prop.kind === SyntaxKind.SpreadAssignment) { context.tracker.reportInferenceFallback(prop); result = false; } else if (prop.name.flags & NodeFlags.ThisNodeHasError) { result = false; break; // Bail if parse errors } else if (prop.name.kind === SyntaxKind.PrivateIdentifier) { // Not valid in object literals but the compiler will complain about this, we just ignore it here. result = false; } else if (prop.name.kind === SyntaxKind.ComputedPropertyName) { const expression = prop.name.expression; if (!isPrimitiveLiteralValue(expression, /*includeBigInt*/ false) && !resolver.isDefinitelyReferenceToGlobalSymbolObject(expression)) { context.tracker.reportInferenceFallback(prop.name); result = false; } } } return result; } function typeFromObjectLiteral(objectLiteral: ObjectLiteralExpression, context: SyntacticTypeNodeBuilderContext, isConstContext: boolean, requiresAddingUndefined: boolean) { if (!canGetTypeFromObjectLiteral(objectLiteral, context)) { if (requiresAddingUndefined || isDeclaration(walkUpParenthesizedExpressions(objectLiteral).parent)) { return alreadyReported; } return syntacticResult(inferExpressionType(objectLiteral, context, /*reportFallback*/ false, requiresAddingUndefined)); } // Disable any inference fallback since we won't actually use the resulting type and we don't want to generate errors const oldNoInferenceFallback = context.noInferenceFallback; context.noInferenceFallback = true; const properties: TypeElement[] = []; const oldFlags = context.flags; context.flags |= NodeBuilderFlags.InObjectTypeLiteral; for (const prop of objectLiteral.properties) { Debug.assert(!isShorthandPropertyAssignment(prop) && !isSpreadAssignment(prop)); const name = prop.name; let newProp; switch (prop.kind) { case SyntaxKind.MethodDeclaration: newProp = withNewScope(context, prop, () => typeFromObjectLiteralMethod(prop, name, context, isConstContext)); break; case SyntaxKind.PropertyAssignment: newProp = typeFromObjectLiteralPropertyAssignment(prop, name, context, isConstContext); break; case SyntaxKind.SetAccessor: case SyntaxKind.GetAccessor: newProp = typeFromObjectLiteralAccessor(prop, name, context); break; } if (newProp) { setCommentRange(newProp, prop); properties.push(newProp); } } context.flags = oldFlags; const typeNode = factory.createTypeLiteralNode(properties); if (!(context.flags & NodeBuilderFlags.MultilineObjectLiterals)) { setEmitFlags(typeNode, EmitFlags.SingleLine); } context.noInferenceFallback = oldNoInferenceFallback; return notImplemented; } function typeFromObjectLiteralPropertyAssignment(prop: PropertyAssignment, name: PropertyName, context: SyntacticTypeNodeBuilderContext, isConstContext: boolean) { const modifiers = isConstContext ? [factory.createModifier(SyntaxKind.ReadonlyKeyword)] : []; const expressionResult = typeFromExpression(prop.initializer, context, isConstContext); const typeNode = expressionResult.type !== undefined ? expressionResult.type : inferTypeOfDeclaration(prop, /*symbol*/ undefined, context, expressionResult.reportFallback); return factory.createPropertySignature( modifiers, reuseNode(context, name), /*questionToken*/ undefined, typeNode, ); } function ensureParameter(p: ParameterDeclaration, context: SyntacticTypeNodeBuilderContext) { return factory.updateParameterDeclaration( p, [], reuseNode(context, p.dotDotDotToken), resolver.serializeNameOfParameter(context, p), resolver.isOptionalParameter(p) ? factory.createToken(SyntaxKind.QuestionToken) : undefined, typeFromParameter(p, /*symbol*/ undefined, context), // Ignore private param props, since this type is going straight back into a param /*initializer*/ undefined, ); } function reuseTypeParameters(typeParameters: NodeArray | undefined, context: SyntacticTypeNodeBuilderContext) { return typeParameters?.map(tp => { const { node: tpName } = resolver.trackExistingEntityName(context, tp.name); return factory.updateTypeParameterDeclaration( tp, tp.modifiers?.map(m => reuseNode(context, m)), tpName, serializeExistingTypeNodeWithFallback(tp.constraint, context), serializeExistingTypeNodeWithFallback(tp.default, context), ); }); } function typeFromObjectLiteralMethod(method: MethodDeclaration, name: PropertyName, context: SyntacticTypeNodeBuilderContext, isConstContext: boolean) { const returnType = createReturnFromSignature(method, /*symbol*/ undefined, context); const typeParameters = reuseTypeParameters(method.typeParameters, context); const parameters = method.parameters.map(p => ensureParameter(p, context)); if (isConstContext) { return factory.createPropertySignature( [factory.createModifier(SyntaxKind.ReadonlyKeyword)], reuseNode(context, name), reuseNode(context, method.questionToken), factory.createFunctionTypeNode( typeParameters, parameters, returnType, ), ); } else { if (isIdentifier(name) && name.escapedText === "new") { name = factory.createStringLiteral("new"); } return factory.createMethodSignature( [], reuseNode(context, name), reuseNode(context, method.questionToken), typeParameters, parameters, returnType, ); } } function typeFromObjectLiteralAccessor(accessor: GetAccessorDeclaration | SetAccessorDeclaration, name: PropertyName, context: SyntacticTypeNodeBuilderContext) { const allAccessors = resolver.getAllAccessorDeclarations(accessor); const getAccessorType = allAccessors.getAccessor && getTypeAnnotationFromAccessor(allAccessors.getAccessor); const setAccessorType = allAccessors.setAccessor && getTypeAnnotationFromAccessor(allAccessors.setAccessor); // We have types for both accessors, we can't know if they are the same type so we keep both accessors if (getAccessorType !== undefined && setAccessorType !== undefined) { return withNewScope(context, accessor, () => { const parameters = accessor.parameters.map(p => ensureParameter(p, context)); if (isGetAccessor(accessor)) { return factory.updateGetAccessorDeclaration( accessor, [], reuseNode(context, name), parameters, serializeExistingTypeNodeWithFallback(getAccessorType, context), /*body*/ undefined, ); } else { return factory.updateSetAccessorDeclaration( accessor, [], reuseNode(context, name), parameters, /*body*/ undefined, ); } }); } else if (allAccessors.firstAccessor === accessor) { const foundType = getAccessorType ? withNewScope(context, allAccessors.getAccessor!, () => serializeExistingTypeNodeWithFallback(getAccessorType, context)) : setAccessorType ? withNewScope(context, allAccessors.setAccessor!, () => serializeExistingTypeNodeWithFallback(setAccessorType, context)) : undefined; const propertyType = foundType ?? inferAccessorType(accessor, allAccessors, context, /*symbol*/ undefined); const propertySignature = factory.createPropertySignature( allAccessors.setAccessor === undefined ? [factory.createModifier(SyntaxKind.ReadonlyKeyword)] : [], reuseNode(context, name), /*questionToken*/ undefined, propertyType, ); return propertySignature; } } function createUndefinedTypeNode() { if (strictNullChecks) { return factory.createKeywordTypeNode(SyntaxKind.UndefinedKeyword); } else { return factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); } } function typeFromPrimitiveLiteral(node: PrimitiveLiteral, baseType: KeywordTypeSyntaxKind, context: SyntacticTypeNodeBuilderContext, preserveLiterals: boolean, requiresAddingUndefined: boolean) { let result; if (preserveLiterals) { if (node.kind === SyntaxKind.PrefixUnaryExpression && node.operator === SyntaxKind.PlusToken) { result = factory.createLiteralTypeNode(reuseNode(context, node.operand)); } result = factory.createLiteralTypeNode(reuseNode(context, node)); } else { result = factory.createKeywordTypeNode(baseType); } return syntacticResult(addUndefinedIfNeeded(result, requiresAddingUndefined, node, context)); } function addUndefinedIfNeeded(node: TypeNode, addUndefined: boolean | undefined, owner: Node | undefined, context: SyntacticTypeNodeBuilderContext) { const parentDeclaration = owner && walkUpParenthesizedExpressions(owner).parent; const optionalDeclaration = parentDeclaration && isDeclaration(parentDeclaration) && isOptionalDeclaration(parentDeclaration); if (!strictNullChecks || !(addUndefined || optionalDeclaration)) return node; if (!canAddUndefined(node)) { context.tracker.reportInferenceFallback(node); } if (isUnionTypeNode(node)) { return factory.createUnionTypeNode([...node.types, factory.createKeywordTypeNode(SyntaxKind.UndefinedKeyword)]); } return factory.createUnionTypeNode([node, factory.createKeywordTypeNode(SyntaxKind.UndefinedKeyword)]); } function canAddUndefined(node: TypeNode): boolean { if (!strictNullChecks) return true; if ( isKeyword(node.kind) || node.kind === SyntaxKind.LiteralType || node.kind === SyntaxKind.FunctionType || node.kind === SyntaxKind.ConstructorType || node.kind === SyntaxKind.ArrayType || node.kind === SyntaxKind.TupleType || node.kind === SyntaxKind.TypeLiteral || node.kind === SyntaxKind.TemplateLiteralType || node.kind === SyntaxKind.ThisType ) { return true; } if (node.kind === SyntaxKind.ParenthesizedType) { return canAddUndefined((node as ParenthesizedTypeNode).type); } if (node.kind === SyntaxKind.UnionType || node.kind === SyntaxKind.IntersectionType) { return (node as UnionTypeNode | IntersectionTypeNode).types.every(canAddUndefined); } return false; } function createReturnFromSignature(fn: SignatureDeclaration | JSDocSignature, symbol: Symbol | undefined, context: SyntacticTypeNodeBuilderContext, reportFallback: boolean = true): TypeNode { let returnType = failed; const returnTypeNode = isJSDocConstructSignature(fn) ? getEffectiveTypeAnnotationNode(fn.parameters[0]) : getEffectiveReturnTypeNode(fn); if (returnTypeNode) { returnType = syntacticResult(serializeTypeAnnotationOfDeclaration(returnTypeNode, context, fn, symbol)); } else if (isValueSignatureDeclaration(fn)) { returnType = typeFromSingleReturnExpression(fn, context); } return returnType.type !== undefined ? returnType.type : inferReturnTypeOfSignatureSignature(fn, context, symbol, reportFallback && returnType.reportFallback && !returnTypeNode); } function typeFromSingleReturnExpression(declaration: FunctionLikeDeclaration | undefined, context: SyntacticTypeNodeBuilderContext): SyntacticResult { let candidateExpr: Expression | undefined; if (declaration && !nodeIsMissing(declaration.body)) { const flags = getFunctionFlags(declaration); if (flags & FunctionFlags.AsyncGenerator) return failed; const body = declaration.body; if (body && isBlock(body)) { forEachReturnStatement(body, s => { if (s.parent !== body) { candidateExpr = undefined; return true; } if (!candidateExpr) { candidateExpr = s.expression; } else { candidateExpr = undefined; return true; } }); } else { candidateExpr = body; } } if (candidateExpr) { if (isContextuallyTyped(candidateExpr)) { const type = isJSDocTypeAssertion(candidateExpr) ? getJSDocTypeAssertionType(candidateExpr) : isAsExpression(candidateExpr) || isTypeAssertionExpression(candidateExpr) ? candidateExpr.type : undefined; if (type && !isConstTypeReference(type)) { return syntacticResult(serializeExistingTypeNode(type, context)); } } else { return typeFromExpression(candidateExpr, context); } } return failed; } function isContextuallyTyped(node: Node) { return findAncestor(node.parent, n => { // Functions calls or parent type annotations (but not the return type of a function expression) may impact the inferred type and local inference is unreliable return isCallExpression(n) || (!isFunctionLikeDeclaration(n) && !!getEffectiveTypeAnnotationNode(n)) || isJsxElement(n) || isJsxExpression(n); }); } }