diff --git a/factory/formatter.ts b/factory/formatter.ts index b77a9b954..43840d86d 100644 --- a/factory/formatter.ts +++ b/factory/formatter.ts @@ -12,7 +12,6 @@ import { ConstructorTypeFormatter } from "../src/TypeFormatter/ConstructorTypeFo import { DefinitionTypeFormatter } from "../src/TypeFormatter/DefinitionTypeFormatter.js"; import { EnumTypeFormatter } from "../src/TypeFormatter/EnumTypeFormatter.js"; import { FunctionTypeFormatter } from "../src/TypeFormatter/FunctionTypeFormatter.js"; -import { HiddenTypeFormatter } from "../src/TypeFormatter/HiddenTypeFormatter.js"; import { IntersectionTypeFormatter } from "../src/TypeFormatter/IntersectionTypeFormatter.js"; import { LiteralTypeFormatter } from "../src/TypeFormatter/LiteralTypeFormatter.js"; import { LiteralUnionTypeFormatter } from "../src/TypeFormatter/LiteralUnionTypeFormatter.js"; @@ -58,7 +57,6 @@ export function createFormatter(config: CompletedConfig, augmentor?: FormatterAu .addTypeFormatter(new UndefinedTypeFormatter()) .addTypeFormatter(new UnknownTypeFormatter()) .addTypeFormatter(new VoidTypeFormatter()) - .addTypeFormatter(new HiddenTypeFormatter()) .addTypeFormatter(new NeverTypeFormatter()) .addTypeFormatter(new LiteralTypeFormatter()) diff --git a/index.ts b/index.ts index 0ec7cf8c6..cea4d7eb6 100644 --- a/index.ts +++ b/index.ts @@ -49,7 +49,6 @@ export * from "./src/Type/BooleanType.js"; export * from "./src/Type/DefinitionType.js"; export * from "./src/Type/EnumType.js"; export * from "./src/Type/FunctionType.js"; -export * from "./src/Type/HiddenType.js"; export * from "./src/Type/IntersectionType.js"; export * from "./src/Type/LiteralType.js"; export * from "./src/Type/NeverType.js"; @@ -83,7 +82,6 @@ export * from "./src/TypeFormatter/ArrayTypeFormatter.js"; export * from "./src/TypeFormatter/BooleanTypeFormatter.js"; export * from "./src/TypeFormatter/DefinitionTypeFormatter.js"; export * from "./src/TypeFormatter/EnumTypeFormatter.js"; -export * from "./src/TypeFormatter/HiddenTypeFormatter.js"; export * from "./src/TypeFormatter/IntersectionTypeFormatter.js"; export * from "./src/TypeFormatter/LiteralTypeFormatter.js"; export * from "./src/TypeFormatter/LiteralUnionTypeFormatter.js"; diff --git a/src/ChainNodeParser.ts b/src/ChainNodeParser.ts index bd8c2c409..01e352e92 100644 --- a/src/ChainNodeParser.ts +++ b/src/ChainNodeParser.ts @@ -7,7 +7,7 @@ import { BaseType } from "./Type/BaseType.js"; import { ReferenceType } from "./Type/ReferenceType.js"; export class ChainNodeParser implements SubNodeParser, MutableParser { - protected readonly typeCaches = new WeakMap>(); + protected readonly typeCaches = new WeakMap>(); public constructor( protected typeChecker: ts.TypeChecker, @@ -23,7 +23,7 @@ export class ChainNodeParser implements SubNodeParser, MutableParser { return this.nodeParsers.some((nodeParser) => nodeParser.supportsNode(node)); } - public createType(node: ts.Node, context: Context, reference?: ReferenceType): BaseType { + public createType(node: ts.Node, context: Context, reference?: ReferenceType): BaseType | undefined { let typeCache = this.typeCaches.get(node); if (typeCache == null) { typeCache = new Map(); diff --git a/src/CircularReferenceNodeParser.ts b/src/CircularReferenceNodeParser.ts index 88244d5f5..ed2532595 100644 --- a/src/CircularReferenceNodeParser.ts +++ b/src/CircularReferenceNodeParser.ts @@ -13,7 +13,7 @@ export class CircularReferenceNodeParser implements SubNodeParser { public supportsNode(node: ts.Node): boolean { return this.childNodeParser.supportsNode(node); } - public createType(node: ts.Node, context: Context): BaseType { + public createType(node: ts.Node, context: Context): BaseType | undefined { const key = getKey(node, context); if (this.circular.has(key)) { return this.circular.get(key)!; diff --git a/src/ExposeNodeParser.ts b/src/ExposeNodeParser.ts index baa9ea5b4..c4d946049 100644 --- a/src/ExposeNodeParser.ts +++ b/src/ExposeNodeParser.ts @@ -19,8 +19,9 @@ export class ExposeNodeParser implements SubNodeParser { return this.subNodeParser.supportsNode(node); } - public createType(node: ts.Node, context: Context, reference?: ReferenceType): BaseType { + public createType(node: ts.Node, context: Context, reference?: ReferenceType): BaseType | undefined { const baseType = this.subNodeParser.createType(node, context, reference); + if (!baseType) return undefined; if (!this.isExportNode(node)) { return baseType; diff --git a/src/NodeParser.ts b/src/NodeParser.ts index b9f944dc3..d87716a09 100644 --- a/src/NodeParser.ts +++ b/src/NodeParser.ts @@ -61,5 +61,5 @@ export class Context { } export interface NodeParser { - createType(node: ts.Node, context: Context, reference?: ReferenceType): BaseType; + createType(node: ts.Node, context: Context, reference?: ReferenceType): BaseType | undefined; } diff --git a/src/NodeParser/AnnotatedNodeParser.ts b/src/NodeParser/AnnotatedNodeParser.ts index f803aea21..a85ca4acb 100644 --- a/src/NodeParser/AnnotatedNodeParser.ts +++ b/src/NodeParser/AnnotatedNodeParser.ts @@ -21,7 +21,7 @@ export class AnnotatedNodeParser implements SubNodeParser { return this.childNodeParser.supportsNode(node); } - public createType(node: ts.Node, context: Context, reference?: ReferenceType): BaseType { + public createType(node: ts.Node, context: Context, reference?: ReferenceType): BaseType | undefined { const annotatedNode = this.getAnnotatedNode(node); let annotations = this.annotationsReader.getAnnotations(annotatedNode); const nullable = this.getNullable(annotatedNode); @@ -33,6 +33,10 @@ export class AnnotatedNodeParser implements SubNodeParser { const baseType = this.childNodeParser.createType(node, context, reference); + if (!baseType) { + return undefined; + } + // Don't return annotations for lib types such as Exclude. // Sourceless nodes may not have a fileName, just ignore them. if (node.getSourceFile()?.fileName.match(/[/\\]typescript[/\\]lib[/\\]lib\.[^/\\]+\.d\.ts$/i)) { diff --git a/src/NodeParser/ArrayLiteralExpressionNodeParser.ts b/src/NodeParser/ArrayLiteralExpressionNodeParser.ts index 0b22b7975..32ea3109e 100644 --- a/src/NodeParser/ArrayLiteralExpressionNodeParser.ts +++ b/src/NodeParser/ArrayLiteralExpressionNodeParser.ts @@ -3,6 +3,7 @@ import { Context, NodeParser } from "../NodeParser.js"; import { SubNodeParser } from "../SubNodeParser.js"; import { BaseType } from "../Type/BaseType.js"; import { TupleType } from "../Type/TupleType.js"; +import { notUndefined } from "../Utils/notUndefined.js"; export class ArrayLiteralExpressionNodeParser implements SubNodeParser { public constructor(protected childNodeParser: NodeParser) {} @@ -12,7 +13,7 @@ export class ArrayLiteralExpressionNodeParser implements SubNodeParser { } public createType(node: ts.ArrayLiteralExpression, context: Context): BaseType { - const elements = node.elements.map((t) => this.childNodeParser.createType(t, context)); + const elements = node.elements.map((t) => this.childNodeParser.createType(t, context)).filter(notUndefined); return new TupleType(elements); } } diff --git a/src/NodeParser/ArrayNodeParser.ts b/src/NodeParser/ArrayNodeParser.ts index 467f635d7..aa48a5201 100644 --- a/src/NodeParser/ArrayNodeParser.ts +++ b/src/NodeParser/ArrayNodeParser.ts @@ -11,8 +11,8 @@ export class ArrayNodeParser implements SubNodeParser { return node.kind === ts.SyntaxKind.ArrayType; } - public createType(node: ts.ArrayTypeNode, context: Context): BaseType { + public createType(node: ts.ArrayTypeNode, context: Context): BaseType | undefined { const type = this.childNodeParser.createType(node.elementType, context); - return new ArrayType(type); + return type && new ArrayType(type); } } diff --git a/src/NodeParser/AsExpressionNodeParser.ts b/src/NodeParser/AsExpressionNodeParser.ts index 68222bdda..608b7fd8f 100644 --- a/src/NodeParser/AsExpressionNodeParser.ts +++ b/src/NodeParser/AsExpressionNodeParser.ts @@ -10,7 +10,7 @@ export class AsExpressionNodeParser implements SubNodeParser { public supportsNode(node: ts.AsExpression): boolean { return node.kind === ts.SyntaxKind.AsExpression; } - public createType(node: ts.AsExpression, context: Context): BaseType { + public createType(node: ts.AsExpression, context: Context): BaseType | undefined { // only implement `as const` for now where we just ignore the as expression return this.childNodeParser.createType(node.expression, context); } diff --git a/src/NodeParser/CallExpressionParser.ts b/src/NodeParser/CallExpressionParser.ts index 26849627d..ac33a8ea2 100644 --- a/src/NodeParser/CallExpressionParser.ts +++ b/src/NodeParser/CallExpressionParser.ts @@ -16,7 +16,7 @@ export class CallExpressionParser implements SubNodeParser { public supportsNode(node: ts.CallExpression): boolean { return node.kind === ts.SyntaxKind.CallExpression; } - public createType(node: ts.CallExpression, context: Context): BaseType { + public createType(node: ts.CallExpression, context: Context): BaseType | undefined { const type = this.typeChecker.getTypeAtLocation(node); // FIXME: remove special case @@ -42,7 +42,7 @@ export class CallExpressionParser implements SubNodeParser { for (const arg of node.arguments) { const type = this.childNodeParser.createType(arg, parentContext); - subContext.pushArgument(type); + type && subContext.pushArgument(type); } return subContext; } diff --git a/src/NodeParser/ConditionalTypeNodeParser.ts b/src/NodeParser/ConditionalTypeNodeParser.ts index 6e05b1bd2..e4a575d7d 100644 --- a/src/NodeParser/ConditionalTypeNodeParser.ts +++ b/src/NodeParser/ConditionalTypeNodeParser.ts @@ -24,11 +24,15 @@ export class ConditionalTypeNodeParser implements SubNodeParser { return node.kind === ts.SyntaxKind.ConditionalType; } - public createType(node: ts.ConditionalTypeNode, context: Context): BaseType { + public createType(node: ts.ConditionalTypeNode, context: Context): BaseType | undefined { const checkType = this.childNodeParser.createType(node.checkType, context); const extendsType = this.childNodeParser.createType(node.extendsType, context); const checkTypeParameterName = this.getTypeParameterName(node.checkType); + if (!checkType || !extendsType) { + return undefined; + } + const inferMap = new Map(); // If check-type is not a type parameter then condition is very simple, no type narrowing needed diff --git a/src/NodeParser/EnumNodeParser.ts b/src/NodeParser/EnumNodeParser.ts index a23a3a49c..b95b6d53b 100644 --- a/src/NodeParser/EnumNodeParser.ts +++ b/src/NodeParser/EnumNodeParser.ts @@ -3,7 +3,6 @@ import { Context } from "../NodeParser.js"; import { SubNodeParser } from "../SubNodeParser.js"; import { BaseType } from "../Type/BaseType.js"; import { EnumType, EnumValue } from "../Type/EnumType.js"; -import { isNodeHidden } from "../Utils/isHidden.js"; import { getKey } from "../Utils/nodeKey.js"; export class EnumNodeParser implements SubNodeParser { @@ -17,9 +16,7 @@ export class EnumNodeParser implements SubNodeParser { return new EnumType( `enum-${getKey(node, context)}`, - members - .filter((member: ts.EnumMember) => !isNodeHidden(member)) - .map((member, index) => this.getMemberValue(member, index)), + members.map((member, index) => this.getMemberValue(member, index)), ); } diff --git a/src/NodeParser/ExpressionWithTypeArgumentsNodeParser.ts b/src/NodeParser/ExpressionWithTypeArgumentsNodeParser.ts index b7e382166..a6cac6b8f 100644 --- a/src/NodeParser/ExpressionWithTypeArgumentsNodeParser.ts +++ b/src/NodeParser/ExpressionWithTypeArgumentsNodeParser.ts @@ -12,7 +12,7 @@ export class ExpressionWithTypeArgumentsNodeParser implements SubNodeParser { public supportsNode(node: ts.ExpressionWithTypeArguments): boolean { return node.kind === ts.SyntaxKind.ExpressionWithTypeArguments; } - public createType(node: ts.ExpressionWithTypeArguments, context: Context): BaseType { + public createType(node: ts.ExpressionWithTypeArguments, context: Context): BaseType | undefined { const typeSymbol = this.typeChecker.getSymbolAtLocation(node.expression)!; if (typeSymbol.flags & ts.SymbolFlags.Alias) { const aliasedSymbol = this.typeChecker.getAliasedSymbol(typeSymbol); @@ -32,7 +32,7 @@ export class ExpressionWithTypeArgumentsNodeParser implements SubNodeParser { if (node.typeArguments?.length) { node.typeArguments.forEach((typeArg) => { const type = this.childNodeParser.createType(typeArg, parentContext); - subContext.pushArgument(type); + type && subContext.pushArgument(type); }); } return subContext; diff --git a/src/NodeParser/FunctionNodeParser.ts b/src/NodeParser/FunctionNodeParser.ts index d75003824..8e183385b 100644 --- a/src/NodeParser/FunctionNodeParser.ts +++ b/src/NodeParser/FunctionNodeParser.ts @@ -8,6 +8,7 @@ import { DefinitionType } from "../Type/DefinitionType.js"; import { Context, NodeParser } from "../NodeParser.js"; import { ObjectProperty, ObjectType } from "../Type/ObjectType.js"; import { getKey } from "../Utils/nodeKey.js"; +import { notUndefined } from "../Utils/notUndefined.js"; export class FunctionNodeParser implements SubNodeParser { constructor( @@ -53,9 +54,9 @@ export function getNamedArguments( return undefined; } - const parameterTypes = node.parameters.map((parameter) => { - return childNodeParser.createType(parameter, context); - }); + const parameterTypes = node.parameters + .map((parameter) => childNodeParser.createType(parameter, context)) + .filter(notUndefined); return new ObjectType( `object-${getKey(node, context)}`, diff --git a/src/NodeParser/HiddenTypeNodeParser.ts b/src/NodeParser/HiddenTypeNodeParser.ts index 681cb35bf..a5f372f80 100644 --- a/src/NodeParser/HiddenTypeNodeParser.ts +++ b/src/NodeParser/HiddenTypeNodeParser.ts @@ -1,8 +1,6 @@ import ts from "typescript"; import { Context } from "../NodeParser.js"; import { SubNodeParser } from "../SubNodeParser.js"; -import { BaseType } from "../Type/BaseType.js"; -import { HiddenType } from "../Type/HiddenType.js"; import { isNodeHidden } from "../Utils/isHidden.js"; export class HiddenNodeParser implements SubNodeParser { @@ -12,7 +10,7 @@ export class HiddenNodeParser implements SubNodeParser { return isNodeHidden(node); } - public createType(_node: ts.KeywordTypeNode, _context: Context): BaseType { - return new HiddenType(); + public createType(_node: ts.KeywordTypeNode, _context: Context): undefined { + return undefined; } } diff --git a/src/NodeParser/IndexedAccessTypeNodeParser.ts b/src/NodeParser/IndexedAccessTypeNodeParser.ts index 2544ae1c5..c424562ff 100644 --- a/src/NodeParser/IndexedAccessTypeNodeParser.ts +++ b/src/NodeParser/IndexedAccessTypeNodeParser.ts @@ -45,15 +45,19 @@ export class IndexedAccessTypeNodeParser implements SubNodeParser { return undefined; } - public createType(node: ts.IndexedAccessTypeNode, context: Context): BaseType { - const indexType = derefType(this.childNodeParser.createType(node.indexType, context)); + public createType(node: ts.IndexedAccessTypeNode, context: Context): BaseType | undefined { + const it = this.childNodeParser.createType(node.indexType, context); + if (!it) return undefined; + const indexType = derefType(it); const indexedType = this.createIndexedType(node.objectType, context, indexType); if (indexedType) { return indexedType; } - const objectType = derefType(this.childNodeParser.createType(node.objectType, context)); + const ot = this.childNodeParser.createType(node.objectType, context); + if (!ot) return undefined; + const objectType = derefType(ot); if (objectType instanceof NeverType || indexType instanceof NeverType) { return new NeverType(); } diff --git a/src/NodeParser/InterfaceAndClassNodeParser.ts b/src/NodeParser/InterfaceAndClassNodeParser.ts index ca8a715b9..bcece8b07 100644 --- a/src/NodeParser/InterfaceAndClassNodeParser.ts +++ b/src/NodeParser/InterfaceAndClassNodeParser.ts @@ -6,9 +6,9 @@ import { BaseType } from "../Type/BaseType.js"; import { NeverType } from "../Type/NeverType.js"; import { ObjectProperty, ObjectType } from "../Type/ObjectType.js"; import { ReferenceType } from "../Type/ReferenceType.js"; -import { isNodeHidden } from "../Utils/isHidden.js"; import { isPublic, isStatic } from "../Utils/modifiers.js"; import { getKey } from "../Utils/nodeKey.js"; +import { notUndefined } from "../Utils/notUndefined.js"; export class InterfaceAndClassNodeParser implements SubNodeParser { public constructor( @@ -25,7 +25,7 @@ export class InterfaceAndClassNodeParser implements SubNodeParser { node: ts.InterfaceDeclaration | ts.ClassDeclaration, context: Context, reference?: ReferenceType, - ): BaseType { + ): BaseType | undefined { if (node.typeParameters?.length) { node.typeParameters.forEach((typeParam) => { const nameSymbol = this.typeChecker.getSymbolAtLocation(typeParam.name)!; @@ -33,7 +33,7 @@ export class InterfaceAndClassNodeParser implements SubNodeParser { if (typeParam.default) { const type = this.childNodeParser.createType(typeParam.default, context); - context.setDefault(nameSymbol.name, type); + type && context.setDefault(nameSymbol.name, type); } }); } @@ -56,7 +56,8 @@ export class InterfaceAndClassNodeParser implements SubNodeParser { if (properties.length === 0 && additionalProperties === false) { const arrayItemType = this.getArrayItemType(node); if (arrayItemType) { - return new ArrayType(this.childNodeParser.createType(arrayItemType, context)); + const type = this.childNodeParser.createType(arrayItemType, context); + return type && new ArrayType(type); } } @@ -95,7 +96,9 @@ export class InterfaceAndClassNodeParser implements SubNodeParser { return node.heritageClauses.reduce( (result: BaseType[], baseType) => [ ...result, - ...baseType.types.map((expression) => this.childNodeParser.createType(expression, context)), + ...baseType.types + .map((expression) => this.childNodeParser.createType(expression, context)) + .filter(notUndefined), ], [], ); @@ -122,7 +125,7 @@ export class InterfaceAndClassNodeParser implements SubNodeParser { }, [] as (ts.PropertyDeclaration | ts.PropertySignature | ts.ParameterPropertyDeclaration)[], ) - .filter((member) => isPublic(member) && !isStatic(member) && !isNodeHidden(member)) + .filter((member) => isPublic(member) && !isStatic(member)) .reduce((entries, member) => { let memberType: ts.Node | undefined = member.type; @@ -138,14 +141,13 @@ export class InterfaceAndClassNodeParser implements SubNodeParser { } return entries; }, []) - .map( - ({ member, memberType }) => - new ObjectProperty( - this.getPropertyName(member.name), - this.childNodeParser.createType(memberType, context), - !member.questionToken, - ), - ) + .map(({ member, memberType }) => { + const type = this.childNodeParser.createType(memberType, context); + return type + ? new ObjectProperty(this.getPropertyName(member.name), type, !member.questionToken) + : undefined; + }) + .filter(notUndefined) .filter((prop) => { const type = prop.getType(); if (prop.isRequired() && type instanceof NeverType) { diff --git a/src/NodeParser/IntersectionNodeParser.ts b/src/NodeParser/IntersectionNodeParser.ts index 317018b9e..d0478780f 100644 --- a/src/NodeParser/IntersectionNodeParser.ts +++ b/src/NodeParser/IntersectionNodeParser.ts @@ -9,6 +9,7 @@ import { derefType } from "../Utils/derefType.js"; import { uniqueTypeArray } from "../Utils/uniqueTypeArray.js"; import { UndefinedType } from "../Type/UndefinedType.js"; import { NeverType } from "../Type/NeverType.js"; +import { notUndefined } from "../Utils/notUndefined.js"; export class IntersectionNodeParser implements SubNodeParser { public constructor( @@ -24,11 +25,11 @@ export class IntersectionNodeParser implements SubNodeParser { const types = node.types.map((subnode) => this.childNodeParser.createType(subnode, context)); // if any type is never, the intersection type resolves to never - if (types.filter((t) => t instanceof NeverType).length) { + if (types.some((t) => t instanceof NeverType)) { return new NeverType(); } - return translate(types); + return translate(types.filter(notUndefined)); } } diff --git a/src/NodeParser/LiteralNodeParser.ts b/src/NodeParser/LiteralNodeParser.ts index 31ea08dea..665d111f9 100644 --- a/src/NodeParser/LiteralNodeParser.ts +++ b/src/NodeParser/LiteralNodeParser.ts @@ -9,7 +9,7 @@ export class LiteralNodeParser implements SubNodeParser { public supportsNode(node: ts.LiteralTypeNode): boolean { return node.kind === ts.SyntaxKind.LiteralType; } - public createType(node: ts.LiteralTypeNode, context: Context): BaseType { + public createType(node: ts.LiteralTypeNode, context: Context): BaseType | undefined { return this.childNodeParser.createType(node.literal, context); } } diff --git a/src/NodeParser/MappedTypeNodeParser.ts b/src/NodeParser/MappedTypeNodeParser.ts index cba966942..d008c80e0 100644 --- a/src/NodeParser/MappedTypeNodeParser.ts +++ b/src/NodeParser/MappedTypeNodeParser.ts @@ -18,6 +18,7 @@ import { derefAnnotatedType, derefType } from "../Utils/derefType.js"; import { getKey } from "../Utils/nodeKey.js"; import { preserveAnnotation } from "../Utils/preserveAnnotation.js"; import { removeUndefined } from "../Utils/removeUndefined.js"; +import { notUndefined } from "../Utils/notUndefined.js"; export class MappedTypeNodeParser implements SubNodeParser { public constructor( @@ -29,8 +30,9 @@ export class MappedTypeNodeParser implements SubNodeParser { return node.kind === ts.SyntaxKind.MappedType; } - public createType(node: ts.MappedTypeNode, context: Context): BaseType { + public createType(node: ts.MappedTypeNode, context: Context): BaseType | undefined { const constraintType = this.childNodeParser.createType(node.typeParameter.constraint!, context); + if (!constraintType) return undefined; const keyListType = derefType(constraintType); const id = `indexed-type-${getKey(node, context)}`; @@ -55,10 +57,11 @@ export class MappedTypeNodeParser implements SubNodeParser { node.type!, this.createSubContext(node, keyListType, context), ); - return type instanceof NeverType ? new NeverType() : new ArrayType(type); + return !type ? undefined : type instanceof NeverType ? new NeverType() : new ArrayType(type); } // Key type widens to `string` const type = this.childNodeParser.createType(node.type!, context); + if (!type) return undefined; // const resultType = type instanceof NeverType ? new NeverType() : new ObjectType(id, [], [], type); const resultType = new ObjectType(id, [], [], type); if (resultType) { @@ -90,13 +93,12 @@ export class MappedTypeNodeParser implements SubNodeParser { } } - protected mapKey(node: ts.MappedTypeNode, rawKey: LiteralType, context: Context): BaseType { + protected mapKey(node: ts.MappedTypeNode, rawKey: LiteralType, context: Context): BaseType | undefined { if (!node.nameType) { return rawKey; } - const key = derefType( - this.childNodeParser.createType(node.nameType, this.createSubContext(node, rawKey, context)), - ); + const type = this.childNodeParser.createType(node.nameType, this.createSubContext(node, rawKey, context)); + const key = type && derefType(type); return key; } @@ -112,6 +114,7 @@ export class MappedTypeNodeParser implements SubNodeParser { node.type!, this.createSubContext(node, key, context), ); + if (!propertyType) return result; let newType = derefAnnotatedType(propertyType); let hasUndefined = false; @@ -142,8 +145,9 @@ export class MappedTypeNodeParser implements SubNodeParser { this.createSubContext(node, new LiteralType(value!), context), ); - return new ObjectProperty(value!.toString(), type, !node.questionToken); - }); + return type && new ObjectProperty(value!.toString(), type, !node.questionToken); + }) + .filter(notUndefined); } protected getAdditionalProperties( diff --git a/src/NodeParser/NamedTupleMemberNodeParser.ts b/src/NodeParser/NamedTupleMemberNodeParser.ts index 168226196..7a432f4d5 100644 --- a/src/NodeParser/NamedTupleMemberNodeParser.ts +++ b/src/NodeParser/NamedTupleMemberNodeParser.ts @@ -14,7 +14,7 @@ export class NamedTupleMemberNodeParser implements SubNodeParser { return node.kind === ts.SyntaxKind.NamedTupleMember; } - public createType(node: ts.NamedTupleMember, context: Context, reference?: ReferenceType): BaseType { + public createType(node: ts.NamedTupleMember, context: Context, reference?: ReferenceType): BaseType | undefined { const baseType = this.childNodeParser.createType(node.type, context, reference); if (baseType instanceof ArrayType && node.getChildAt(0).kind === ts.SyntaxKind.DotDotDotToken) { diff --git a/src/NodeParser/ObjectLiteralExpressionNodeParser.ts b/src/NodeParser/ObjectLiteralExpressionNodeParser.ts index 30dc25889..840986711 100644 --- a/src/NodeParser/ObjectLiteralExpressionNodeParser.ts +++ b/src/NodeParser/ObjectLiteralExpressionNodeParser.ts @@ -5,6 +5,7 @@ import { SubNodeParser } from "../SubNodeParser.js"; import { BaseType } from "../Type/BaseType.js"; import { getKey } from "../Utils/nodeKey.js"; import { ObjectProperty, ObjectType } from "../Type/ObjectType.js"; +import { notUndefined } from "../Utils/notUndefined.js"; export class ObjectLiteralExpressionNodeParser implements SubNodeParser { public constructor(protected childNodeParser: NodeParser) {} @@ -14,14 +15,12 @@ export class ObjectLiteralExpressionNodeParser implements SubNodeParser { } public createType(node: ts.ObjectLiteralExpression, context: Context): BaseType { - const properties = node.properties.map( - (t) => - new ObjectProperty( - t.name!.getText(), - this.childNodeParser.createType((t as any).initializer, context), - !(t as any).questionToken, - ), - ); + const properties = node.properties + .map((t) => { + const type = this.childNodeParser.createType((t as any).initializer, context); + return type ? new ObjectProperty(t.name!.getText(), type, !(t as any).questionToken) : undefined; + }) + .filter(notUndefined); return new ObjectType(`object-${getKey(node, context)}`, [], properties, false); } diff --git a/src/NodeParser/OptionalTypeNodeParser.ts b/src/NodeParser/OptionalTypeNodeParser.ts index 4eeab8c64..dbb3838b7 100644 --- a/src/NodeParser/OptionalTypeNodeParser.ts +++ b/src/NodeParser/OptionalTypeNodeParser.ts @@ -9,8 +9,8 @@ export class OptionalTypeNodeParser implements SubNodeParser { public supportsNode(node: ts.OptionalTypeNode): boolean { return node.kind === ts.SyntaxKind.OptionalType; } - public createType(node: ts.OptionalTypeNode, context: Context): BaseType { + public createType(node: ts.OptionalTypeNode, context: Context): BaseType | undefined { const type = this.childNodeParser.createType(node.type, context); - return new OptionalType(type); + return type && new OptionalType(type); } } diff --git a/src/NodeParser/ParameterParser.ts b/src/NodeParser/ParameterParser.ts index bfb190727..547df96ff 100644 --- a/src/NodeParser/ParameterParser.ts +++ b/src/NodeParser/ParameterParser.ts @@ -10,7 +10,7 @@ export class ParameterParser implements SubNodeParser { public supportsNode(node: ts.ParameterDeclaration): boolean { return node.kind === ts.SyntaxKind.Parameter; } - public createType(node: ts.FunctionTypeNode, context: Context): BaseType { + public createType(node: ts.FunctionTypeNode, context: Context): BaseType | undefined { return this.childNodeParser.createType(node.type, context); } } diff --git a/src/NodeParser/ParenthesizedNodeParser.ts b/src/NodeParser/ParenthesizedNodeParser.ts index 5b83e965a..8664d7a84 100644 --- a/src/NodeParser/ParenthesizedNodeParser.ts +++ b/src/NodeParser/ParenthesizedNodeParser.ts @@ -9,7 +9,7 @@ export class ParenthesizedNodeParser implements SubNodeParser { public supportsNode(node: ts.ParenthesizedTypeNode): boolean { return node.kind === ts.SyntaxKind.ParenthesizedType; } - public createType(node: ts.ParenthesizedTypeNode, context: Context): BaseType { + public createType(node: ts.ParenthesizedTypeNode, context: Context): BaseType | undefined { return this.childNodeParser.createType(node.type, context); } } diff --git a/src/NodeParser/PropertyAccessExpressionParser.ts b/src/NodeParser/PropertyAccessExpressionParser.ts index 2dec5c6bf..c8e413d99 100644 --- a/src/NodeParser/PropertyAccessExpressionParser.ts +++ b/src/NodeParser/PropertyAccessExpressionParser.ts @@ -13,7 +13,7 @@ export class PropertyAccessExpressionParser implements SubNodeParser { return node.kind === ts.SyntaxKind.PropertyAccessExpression; } - public createType(node: ts.PropertyAccessExpression, context: Context): BaseType { + public createType(node: ts.PropertyAccessExpression, context: Context): BaseType | undefined { const type = this.typeChecker.getTypeAtLocation(node); return this.childNodeParser.createType(type.symbol.declarations![0], context); } diff --git a/src/NodeParser/SatisfiesNodeParser.ts b/src/NodeParser/SatisfiesNodeParser.ts index 1553b0ec0..784bdf9b8 100644 --- a/src/NodeParser/SatisfiesNodeParser.ts +++ b/src/NodeParser/SatisfiesNodeParser.ts @@ -9,7 +9,7 @@ export class SatisfiesNodeParser implements SubNodeParser { public supportsNode(node: ts.SatisfiesExpression): boolean { return node.kind === ts.SyntaxKind.SatisfiesExpression; } - public createType(node: ts.SatisfiesExpression, context: Context): BaseType { + public createType(node: ts.SatisfiesExpression, context: Context): BaseType | undefined { return this.childNodeParser.createType(node.expression, context); } } diff --git a/src/NodeParser/StringTemplateLiteralNodeParser.ts b/src/NodeParser/StringTemplateLiteralNodeParser.ts index 0ecf642f4..d80a33f36 100644 --- a/src/NodeParser/StringTemplateLiteralNodeParser.ts +++ b/src/NodeParser/StringTemplateLiteralNodeParser.ts @@ -7,6 +7,7 @@ import { LiteralType } from "../Type/LiteralType.js"; import { StringType } from "../Type/StringType.js"; import { UnionType } from "../Type/UnionType.js"; import { extractLiterals } from "../Utils/extractLiterals.js"; +import { notUndefined } from "../Utils/notUndefined.js"; export class StringTemplateLiteralNodeParser implements SubNodeParser { public constructor(protected childNodeParser: NodeParser) {} @@ -24,11 +25,13 @@ export class StringTemplateLiteralNodeParser implements SubNodeParser { try { const prefix = node.head.text; const matrix: string[][] = [[prefix]].concat( - node.templateSpans.map((span) => { - const suffix = span.literal.text; - const type = this.childNodeParser.createType(span.type, context); - return extractLiterals(type).map((value) => value + suffix); - }), + node.templateSpans + .map((span) => { + const suffix = span.literal.text; + const type = this.childNodeParser.createType(span.type, context); + return type && extractLiterals(type).map((value) => value + suffix); + }) + .filter(notUndefined), ); const expandedLiterals = expand(matrix); diff --git a/src/NodeParser/TupleNodeParser.ts b/src/NodeParser/TupleNodeParser.ts index 281e8aa6e..086842bc2 100644 --- a/src/NodeParser/TupleNodeParser.ts +++ b/src/NodeParser/TupleNodeParser.ts @@ -3,6 +3,7 @@ import { Context, NodeParser } from "../NodeParser.js"; import { SubNodeParser } from "../SubNodeParser.js"; import { BaseType } from "../Type/BaseType.js"; import { TupleType } from "../Type/TupleType.js"; +import { notUndefined } from "../Utils/notUndefined.js"; export class TupleNodeParser implements SubNodeParser { public constructor( @@ -16,9 +17,7 @@ export class TupleNodeParser implements SubNodeParser { public createType(node: ts.TupleTypeNode, context: Context): BaseType { return new TupleType( - node.elements.map((item) => { - return this.childNodeParser.createType(item, context); - }), + node.elements.map((item) => this.childNodeParser.createType(item, context)).filter(notUndefined), ); } } diff --git a/src/NodeParser/TypeAliasNodeParser.ts b/src/NodeParser/TypeAliasNodeParser.ts index 5787c04a3..d91cb4343 100644 --- a/src/NodeParser/TypeAliasNodeParser.ts +++ b/src/NodeParser/TypeAliasNodeParser.ts @@ -17,7 +17,11 @@ export class TypeAliasNodeParser implements SubNodeParser { return node.kind === ts.SyntaxKind.TypeAliasDeclaration; } - public createType(node: ts.TypeAliasDeclaration, context: Context, reference?: ReferenceType): BaseType { + public createType( + node: ts.TypeAliasDeclaration, + context: Context, + reference?: ReferenceType, + ): BaseType | undefined { if (node.typeParameters?.length) { for (const typeParam of node.typeParameters) { const nameSymbol = this.typeChecker.getSymbolAtLocation(typeParam.name)!; @@ -25,7 +29,7 @@ export class TypeAliasNodeParser implements SubNodeParser { if (typeParam.default) { const type = this.childNodeParser.createType(typeParam.default, context); - context.setDefault(nameSymbol.name, type); + type && context.setDefault(nameSymbol.name, type); } } } @@ -41,7 +45,7 @@ export class TypeAliasNodeParser implements SubNodeParser { if (type instanceof NeverType) { return new NeverType(); } - return new AliasType(id, type); + return type && new AliasType(id, type); } protected getTypeId(node: ts.TypeAliasDeclaration, context: Context): string { diff --git a/src/NodeParser/TypeLiteralNodeParser.ts b/src/NodeParser/TypeLiteralNodeParser.ts index 4c1b4a5ea..b9dc76b0f 100644 --- a/src/NodeParser/TypeLiteralNodeParser.ts +++ b/src/NodeParser/TypeLiteralNodeParser.ts @@ -5,8 +5,8 @@ import { BaseType } from "../Type/BaseType.js"; import { NeverType } from "../Type/NeverType.js"; import { ObjectProperty, ObjectType } from "../Type/ObjectType.js"; import { ReferenceType } from "../Type/ReferenceType.js"; -import { isNodeHidden } from "../Utils/isHidden.js"; import { getKey } from "../Utils/nodeKey.js"; +import { notUndefined } from "../Utils/notUndefined.js"; export class TypeLiteralNodeParser implements SubNodeParser { public constructor( @@ -42,15 +42,14 @@ export class TypeLiteralNodeParser implements SubNodeParser { (element): element is PropertySignature | MethodSignature => ts.isPropertySignature(element) || ts.isMethodSignature(element), ) - .filter((propertyNode) => !isNodeHidden(propertyNode)) - .map( - (propertyNode) => - new ObjectProperty( - this.getPropertyName(propertyNode.name), - this.childNodeParser.createType(propertyNode.type!, context), - !propertyNode.questionToken, - ), - ) + .map((propertyNode) => { + const type = this.childNodeParser.createType(propertyNode.type!, context); + return ( + type && + new ObjectProperty(this.getPropertyName(propertyNode.name), type, !propertyNode.questionToken) + ); + }) + .filter(notUndefined) .filter((prop) => { const type = prop.getType(); if (prop.isRequired() && type instanceof NeverType) { diff --git a/src/NodeParser/TypeOperatorNodeParser.ts b/src/NodeParser/TypeOperatorNodeParser.ts index 78a9c16c2..0018cd876 100644 --- a/src/NodeParser/TypeOperatorNodeParser.ts +++ b/src/NodeParser/TypeOperatorNodeParser.ts @@ -17,8 +17,9 @@ export class TypeOperatorNodeParser implements SubNodeParser { return node.kind === ts.SyntaxKind.TypeOperator; } - public createType(node: ts.TypeOperatorNode, context: Context): BaseType { + public createType(node: ts.TypeOperatorNode, context: Context): BaseType | undefined { const type = this.childNodeParser.createType(node.type, context); + if (!type) return undefined; const derefed = derefType(type); // Remove readonly modifier from type if (node.operator === ts.SyntaxKind.ReadonlyKeyword && derefed) { diff --git a/src/NodeParser/TypeReferenceNodeParser.ts b/src/NodeParser/TypeReferenceNodeParser.ts index eb90ee253..54a1b1422 100644 --- a/src/NodeParser/TypeReferenceNodeParser.ts +++ b/src/NodeParser/TypeReferenceNodeParser.ts @@ -23,7 +23,7 @@ export class TypeReferenceNodeParser implements SubNodeParser { return node.kind === ts.SyntaxKind.TypeReference; } - public createType(node: ts.TypeReferenceNode, context: Context): BaseType { + public createType(node: ts.TypeReferenceNode, context: Context): BaseType | undefined { const typeSymbol = this.typeChecker.getSymbolAtLocation(node.typeName) ?? // When the node doesn't have a valid source file, its position is -1, so we can't @@ -82,7 +82,8 @@ export class TypeReferenceNodeParser implements SubNodeParser { if (node.typeArguments?.length) { for (const typeArg of node.typeArguments) { - subContext.pushArgument(this.childNodeParser.createType(typeArg, parentContext)); + const type = this.childNodeParser.createType(typeArg, parentContext); + type && subContext.pushArgument(type); } } diff --git a/src/NodeParser/TypeofNodeParser.ts b/src/NodeParser/TypeofNodeParser.ts index cd5ee6200..7c643eb05 100644 --- a/src/NodeParser/TypeofNodeParser.ts +++ b/src/NodeParser/TypeofNodeParser.ts @@ -9,6 +9,7 @@ import { getKey } from "../Utils/nodeKey.js"; import { LiteralType } from "../Type/LiteralType.js"; import { NeverType } from "../Type/NeverType.js"; import { FunctionType } from "../Type/FunctionType.js"; +import { notUndefined } from "../Utils/notUndefined.js"; export class TypeofNodeParser implements SubNodeParser { public constructor( @@ -20,7 +21,7 @@ export class TypeofNodeParser implements SubNodeParser { return node.kind === ts.SyntaxKind.TypeQuery; } - public createType(node: ts.TypeQueryNode, context: Context, reference?: ReferenceType): BaseType { + public createType(node: ts.TypeQueryNode, context: Context, reference?: ReferenceType): BaseType | undefined { let symbol = this.typeChecker.getSymbolAtLocation(node.exprName)!; if (symbol.flags & ts.SymbolFlags.Alias) { symbol = this.typeChecker.getAliasedSymbol(symbol); @@ -64,20 +65,22 @@ export class TypeofNodeParser implements SubNodeParser { reference.setName(id); } - let type: BaseType | null = null; - const properties = node.members.map((member) => { - const name = member.name.getText(); - if (member.initializer) { - type = this.childNodeParser.createType(member.initializer, context); - } else if (type === null) { - type = new LiteralType(0); - } else if (type instanceof LiteralType && typeof type.getValue() === "number") { - type = new LiteralType(+type.getValue() + 1); - } else { - throw new LogicError(`Enum initializer missing for "${name}"`); - } - return new ObjectProperty(name, type, true); - }); + let type: BaseType | null | undefined = null; + const properties = node.members + .map((member) => { + const name = member.name.getText(); + if (member.initializer) { + type = this.childNodeParser.createType(member.initializer, context); + } else if (type === null) { + type = new LiteralType(0); + } else if (type instanceof LiteralType && typeof type.getValue() === "number") { + type = new LiteralType(+type.getValue() + 1); + } else { + throw new LogicError(`Enum initializer missing for "${name}"`); + } + return type && new ObjectProperty(name, type, true); + }) + .filter(notUndefined); return new ObjectType(id, [], properties, false); } diff --git a/src/NodeParser/UnionNodeParser.ts b/src/NodeParser/UnionNodeParser.ts index 656418c0a..e576f6b38 100644 --- a/src/NodeParser/UnionNodeParser.ts +++ b/src/NodeParser/UnionNodeParser.ts @@ -5,6 +5,7 @@ import { UnionType } from "../Type/UnionType.js"; import { BaseType } from "../Type/BaseType.js"; import { notNever } from "../Utils/notNever.js"; import { NeverType } from "../Type/NeverType.js"; +import { notUndefined } from "../Utils/notUndefined.js"; export class UnionNodeParser implements SubNodeParser { public constructor( @@ -21,7 +22,8 @@ export class UnionNodeParser implements SubNodeParser { .map((subnode) => { return this.childNodeParser.createType(subnode, context); }) - .filter(notNever); + .filter(notNever) + .filter(notUndefined); if (types.length === 1) { return types[0]; diff --git a/src/SchemaGenerator.ts b/src/SchemaGenerator.ts index 723aaa7c9..e7020b187 100644 --- a/src/SchemaGenerator.ts +++ b/src/SchemaGenerator.ts @@ -11,6 +11,7 @@ import { localSymbolAtNode, symbolAtNode } from "./Utils/symbolAtNode.js"; import { removeUnreachable } from "./Utils/removeUnreachable.js"; import { Config } from "./Config.js"; import { hasJsDocTag } from "./Utils/hasJsDocTag.js"; +import { notUndefined } from "./Utils/notUndefined.js"; export class SchemaGenerator { public constructor( @@ -26,9 +27,9 @@ export class SchemaGenerator { } public createSchemaFromNodes(rootNodes: ts.Node[]): Schema { - const rootTypes = rootNodes.map((rootNode) => { - return this.nodeParser.createType(rootNode, new Context()); - }); + const rootTypes = rootNodes + .map((rootNode) => this.nodeParser.createType(rootNode, new Context())) + .filter(notUndefined); const rootTypeDefinition = rootTypes.length === 1 ? this.getRootTypeDefinition(rootTypes[0]) : undefined; const definitions: StringMap = {}; diff --git a/src/TopRefNodeParser.ts b/src/TopRefNodeParser.ts index dec37f697..968d9023a 100644 --- a/src/TopRefNodeParser.ts +++ b/src/TopRefNodeParser.ts @@ -10,8 +10,9 @@ export class TopRefNodeParser implements NodeParser { protected topRef: boolean, ) {} - public createType(node: ts.Node, context: Context): BaseType { + public createType(node: ts.Node, context: Context): BaseType | undefined { const baseType = this.childNodeParser.createType(node, context); + if (!baseType) return undefined; if (this.topRef && !(baseType instanceof DefinitionType)) { return new DefinitionType(this.fullName, baseType); diff --git a/src/Type/HiddenType.ts b/src/Type/HiddenType.ts deleted file mode 100644 index ac919856e..000000000 --- a/src/Type/HiddenType.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { NeverType } from "./NeverType.js"; - -export class HiddenType extends NeverType { - public getId(): string { - return "hidden"; - } -} diff --git a/src/TypeFormatter/HiddenTypeFormatter.ts b/src/TypeFormatter/HiddenTypeFormatter.ts deleted file mode 100644 index 30cd6dc97..000000000 --- a/src/TypeFormatter/HiddenTypeFormatter.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Definition } from "../Schema/Definition.js"; -import { SubTypeFormatter } from "../SubTypeFormatter.js"; -import { BaseType } from "../Type/BaseType.js"; -import { HiddenType } from "../Type/HiddenType.js"; - -export class HiddenTypeFormatter implements SubTypeFormatter { - public supportsType(type: BaseType): boolean { - return type instanceof HiddenType; - } - public getDefinition(type: HiddenType): Definition { - return { additionalProperties: false }; - } - public getChildren(type: HiddenType): BaseType[] { - return []; - } -} diff --git a/src/Utils/notUndefined.ts b/src/Utils/notUndefined.ts new file mode 100644 index 000000000..0353c7df6 --- /dev/null +++ b/src/Utils/notUndefined.ts @@ -0,0 +1,3 @@ +export function notUndefined(value: T | undefined): value is T { + return value !== undefined; +} diff --git a/test/unit/Type/HiddenType.test.ts b/test/unit/Type/HiddenType.test.ts deleted file mode 100644 index 6324b9ce5..000000000 --- a/test/unit/Type/HiddenType.test.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { HiddenType } from "../../../src/Type/HiddenType.js"; -import { NeverType } from "../../../src/Type/NeverType.js"; - -describe("HiddenType", () => { - it("creates a HiddenType", () => { - const hidden = new HiddenType(); - expect(hidden instanceof NeverType).toBe(true); - expect(hidden.getId()).toBe("hidden"); - }); -});