Skip to content

Commit

Permalink
fix: consistently use NeverType (#1367)
Browse files Browse the repository at this point in the history
  • Loading branch information
daanboer committed Aug 19, 2022
1 parent 2d3fa23 commit 3ded513
Show file tree
Hide file tree
Showing 51 changed files with 193 additions and 245 deletions.
2 changes: 1 addition & 1 deletion index.ts
Expand Up @@ -22,7 +22,7 @@ export * from "./src/Utils/isHidden";
export * from "./src/Utils/modifiers";
export * from "./src/Utils/narrowType";
export * from "./src/Utils/nodeKey";
export * from "./src/Utils/notUndefined";
export * from "./src/Utils/notNever";
export * from "./src/Utils/preserveAnnotation";
export * from "./src/Utils/removeUndefined";
export * from "./src/Utils/removeUnreachable";
Expand Down
6 changes: 3 additions & 3 deletions src/ChainNodeParser.ts
Expand Up @@ -7,7 +7,7 @@ import { BaseType } from "./Type/BaseType";
import { ReferenceType } from "./Type/ReferenceType";

export class ChainNodeParser implements SubNodeParser, MutableParser {
protected readonly typeCaches = new WeakMap<ts.Node, Map<string, BaseType | undefined>>();
protected readonly typeCaches = new WeakMap<ts.Node, Map<string, BaseType>>();

public constructor(protected typeChecker: ts.TypeChecker, protected nodeParsers: SubNodeParser[]) {}

Expand All @@ -20,10 +20,10 @@ export class ChainNodeParser implements SubNodeParser, MutableParser {
return this.nodeParsers.some((nodeParser) => nodeParser.supportsNode(node));
}

public createType(node: ts.Node, context: Context, reference?: ReferenceType): BaseType | undefined {
public createType(node: ts.Node, context: Context, reference?: ReferenceType): BaseType {
let typeCache = this.typeCaches.get(node);
if (typeCache == null) {
typeCache = new Map<string, BaseType | undefined>();
typeCache = new Map<string, BaseType>();
this.typeCaches.set(node, typeCache);
}
const contextCacheKey = context.getCacheKey();
Expand Down
4 changes: 2 additions & 2 deletions src/CircularReferenceNodeParser.ts
Expand Up @@ -6,14 +6,14 @@ import { ReferenceType } from "./Type/ReferenceType";
import { getKey } from "./Utils/nodeKey";

export class CircularReferenceNodeParser implements SubNodeParser {
protected circular = new Map<string, BaseType>();
protected circular: Map<string, BaseType> = new Map();

public constructor(protected childNodeParser: SubNodeParser) {}

public supportsNode(node: ts.Node): boolean {
return this.childNodeParser.supportsNode(node);
}
public createType(node: ts.Node, context: Context): BaseType | undefined {
public createType(node: ts.Node, context: Context): BaseType {
const key = getKey(node, context);
if (this.circular.has(key)) {
return this.circular.get(key)!;
Expand Down
4 changes: 2 additions & 2 deletions src/CircularReferenceTypeFormatter.ts
Expand Up @@ -4,8 +4,8 @@ import { BaseType } from "./Type/BaseType";
import { uniqueArray } from "./Utils/uniqueArray";

export class CircularReferenceTypeFormatter implements SubTypeFormatter {
protected definition = new Map<BaseType, Definition>();
protected children = new Map<BaseType, BaseType[]>();
protected definition: Map<BaseType, Definition> = new Map();
protected children: Map<BaseType, BaseType[]> = new Map();

public constructor(protected childTypeFormatter: SubTypeFormatter) {}

Expand Down
6 changes: 3 additions & 3 deletions src/Error/UnknownTypeError.ts
Expand Up @@ -2,11 +2,11 @@ import { BaseType } from "../Type/BaseType";
import { BaseError } from "./BaseError";

export class UnknownTypeError extends BaseError {
public constructor(private type: BaseType | undefined) {
super(`Unknown type "${type ? type.getId() : undefined}"`);
public constructor(private type: BaseType) {
super(`Unknown type "${type.getId()}"`);
}

public getType(): BaseType | undefined {
public getType(): BaseType {
return this.type;
}
}
6 changes: 1 addition & 5 deletions src/ExposeNodeParser.ts
Expand Up @@ -19,13 +19,9 @@ export class ExposeNodeParser implements SubNodeParser {
return this.subNodeParser.supportsNode(node);
}

public createType(node: ts.Node, context: Context, reference?: ReferenceType): BaseType | undefined {
public createType(node: ts.Node, context: Context, reference?: ReferenceType): BaseType {
const baseType = this.subNodeParser.createType(node, context, reference);

if (baseType === undefined) {
return undefined;
}

if (!this.isExportNode(node)) {
return baseType;
}
Expand Down
15 changes: 8 additions & 7 deletions src/NodeParser.ts
Expand Up @@ -6,16 +6,16 @@ import { getKey } from "./Utils/nodeKey";

export class Context {
private cacheKey: string | null = null;
private arguments: (BaseType | undefined)[] = [];
private arguments: BaseType[] = [];
private parameters: string[] = [];
private reference?: ts.Node;
private defaultArgument = new Map<string, BaseType | undefined>();
private defaultArgument = new Map<string, BaseType>();

public constructor(reference?: ts.Node) {
this.reference = reference;
}

public pushArgument(argumentType: BaseType | undefined): void {
public pushArgument(argumentType: BaseType): void {
this.arguments.push(argumentType);
this.cacheKey = null;
}
Expand All @@ -24,7 +24,7 @@ export class Context {
this.parameters.push(parameterName);
}

public setDefault(parameterName: string, argumentType: BaseType | undefined): void {
public setDefault(parameterName: string, argumentType: BaseType): void {
this.defaultArgument.set(parameterName, argumentType);
}

Expand All @@ -38,8 +38,9 @@ export class Context {
return this.cacheKey;
}

public getArgument(parameterName: string): BaseType | undefined {
public getArgument(parameterName: string): BaseType {
const index: number = this.parameters.indexOf(parameterName);

if ((index < 0 || !this.arguments[index]) && this.defaultArgument.has(parameterName)) {
return this.defaultArgument.get(parameterName)!;
}
Expand All @@ -50,7 +51,7 @@ export class Context {
public getParameters(): readonly string[] {
return this.parameters;
}
public getArguments(): readonly (BaseType | undefined)[] {
public getArguments(): readonly BaseType[] {
return this.arguments;
}

Expand All @@ -60,5 +61,5 @@ export class Context {
}

export interface NodeParser {
createType(node: ts.Node, context: Context, reference?: ReferenceType): BaseType | undefined;
createType(node: ts.Node, context: Context, reference?: ReferenceType): BaseType;
}
6 changes: 1 addition & 5 deletions src/NodeParser/AnnotatedNodeParser.ts
Expand Up @@ -18,7 +18,7 @@ export class AnnotatedNodeParser implements SubNodeParser {
return this.childNodeParser.supportsNode(node);
}

public createType(node: ts.Node, context: Context, reference?: ReferenceType): BaseType | undefined {
public createType(node: ts.Node, context: Context, reference?: ReferenceType): BaseType {
const annotatedNode = this.getAnnotatedNode(node);
let annotations = this.annotationsReader.getAnnotations(annotatedNode);
const nullable = this.getNullable(annotatedNode);
Expand All @@ -30,10 +30,6 @@ export class AnnotatedNodeParser implements SubNodeParser {

const baseType = this.childNodeParser.createType(node, context, reference);

if (baseType === undefined) {
return undefined;
}

// Don't return annotations for lib types such as Exclude.
if (node.getSourceFile().fileName.match(/[/\\]typescript[/\\]lib[/\\]lib\.[^/\\]+\.d\.ts$/i)) {
let specialCase = false;
Expand Down
13 changes: 3 additions & 10 deletions src/NodeParser/ArrayLiteralExpressionNodeParser.ts
Expand Up @@ -2,7 +2,6 @@ import ts from "typescript";
import { Context, NodeParser } from "../NodeParser";
import { SubNodeParser } from "../SubNodeParser";
import { BaseType } from "../Type/BaseType";
import { notUndefined } from "../Utils/notUndefined";
import { TupleType } from "../Type/TupleType";

export class ArrayLiteralExpressionNodeParser implements SubNodeParser {
Expand All @@ -12,14 +11,8 @@ export class ArrayLiteralExpressionNodeParser implements SubNodeParser {
return node.kind === ts.SyntaxKind.ArrayLiteralExpression;
}

public createType(node: ts.ArrayLiteralExpression, context: Context): BaseType | undefined {
if (node.elements) {
const elements = node.elements.map((t) => this.childNodeParser.createType(t, context)).filter(notUndefined);

return new TupleType(elements);
}

// TODO: implement this?
return undefined;
public createType(node: ts.ArrayLiteralExpression, context: Context): BaseType {
const elements = node.elements.map((t) => this.childNodeParser.createType(t, context));
return new TupleType(elements);
}
}
5 changes: 1 addition & 4 deletions src/NodeParser/ArrayNodeParser.ts
Expand Up @@ -11,11 +11,8 @@ export class ArrayNodeParser implements SubNodeParser {
return node.kind === ts.SyntaxKind.ArrayType;
}

public createType(node: ts.ArrayTypeNode, context: Context): BaseType | undefined {
public createType(node: ts.ArrayTypeNode, context: Context): BaseType {
const type = this.childNodeParser.createType(node.elementType, context);
if (type === undefined) {
return undefined;
}
return new ArrayType(type);
}
}
2 changes: 1 addition & 1 deletion src/NodeParser/AsExpressionNodeParser.ts
Expand Up @@ -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 | undefined {
public createType(node: ts.AsExpression, context: Context): BaseType {
// only implement `as const` for now where we just ignore the as expression
return this.childNodeParser.createType(node.expression, context);
}
Expand Down
9 changes: 5 additions & 4 deletions src/NodeParser/ConditionalTypeNodeParser.ts
Expand Up @@ -5,9 +5,10 @@ import { BaseType } from "../Type/BaseType";
import { isAssignableTo } from "../Utils/isAssignableTo";
import { narrowType } from "../Utils/narrowType";
import { UnionType } from "../Type/UnionType";
import { NeverType } from "../Type/NeverType";

class CheckType {
constructor(public parameterName: string, public type: BaseType | undefined) {}
constructor(public parameterName: string, public type: BaseType) {}
}

export class ConditionalTypeNodeParser implements SubNodeParser {
Expand All @@ -17,7 +18,7 @@ export class ConditionalTypeNodeParser implements SubNodeParser {
return node.kind === ts.SyntaxKind.ConditionalType;
}

public createType(node: ts.ConditionalTypeNode, context: Context): BaseType | undefined {
public createType(node: ts.ConditionalTypeNode, context: Context): BaseType {
const checkType = this.childNodeParser.createType(node.checkType, context);
const extendsType = this.childNodeParser.createType(node.extendsType, context);
const checkTypeParameterName = this.getTypeParameterName(node.checkType);
Expand All @@ -39,7 +40,7 @@ export class ConditionalTypeNodeParser implements SubNodeParser {

// Follow the relevant branches and return the results from them
const results: BaseType[] = [];
if (trueCheckType !== undefined) {
if (!(trueCheckType instanceof NeverType)) {
const result = this.childNodeParser.createType(
node.trueType,
this.createSubContext(node, context, new CheckType(checkTypeParameterName, trueCheckType), inferMap)
Expand All @@ -48,7 +49,7 @@ export class ConditionalTypeNodeParser implements SubNodeParser {
results.push(result);
}
}
if (falseCheckType !== undefined) {
if (!(falseCheckType instanceof NeverType)) {
const result = this.childNodeParser.createType(
node.falseType,
this.createSubContext(node, context, new CheckType(checkTypeParameterName, falseCheckType))
Expand Down
2 changes: 1 addition & 1 deletion src/NodeParser/ExpressionWithTypeArgumentsNodeParser.ts
Expand Up @@ -9,7 +9,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 | undefined {
public createType(node: ts.ExpressionWithTypeArguments, context: Context): BaseType {
const typeSymbol = this.typeChecker.getSymbolAtLocation(node.expression)!;
if (typeSymbol.flags & ts.SymbolFlags.Alias) {
const aliasedSymbol = this.typeChecker.getAliasedSymbol(typeSymbol);
Expand Down
5 changes: 3 additions & 2 deletions src/NodeParser/HiddenTypeNodeParser.ts
Expand Up @@ -2,6 +2,7 @@ import ts from "typescript";
import { Context } from "../NodeParser";
import { SubNodeParser } from "../SubNodeParser";
import { BaseType } from "../Type/BaseType";
import { NeverType } from "../Type/NeverType";
import { isNodeHidden } from "../Utils/isHidden";

export class HiddenNodeParser implements SubNodeParser {
Expand All @@ -11,7 +12,7 @@ export class HiddenNodeParser implements SubNodeParser {
return isNodeHidden(node);
}

public createType(node: ts.KeywordTypeNode, context: Context): BaseType | undefined {
return undefined;
public createType(_node: ts.KeywordTypeNode, _context: Context): BaseType {
return new NeverType();
}
}
11 changes: 6 additions & 5 deletions src/NodeParser/IndexedAccessTypeNodeParser.ts
Expand Up @@ -4,6 +4,7 @@ import { Context, NodeParser } from "../NodeParser";
import { SubNodeParser } from "../SubNodeParser";
import { BaseType } from "../Type/BaseType";
import { LiteralType } from "../Type/LiteralType";
import { NeverType } from "../Type/NeverType";
import { NumberType } from "../Type/NumberType";
import { StringType } from "../Type/StringType";
import { TupleType } from "../Type/TupleType";
Expand All @@ -18,7 +19,7 @@ export class IndexedAccessTypeNodeParser implements SubNodeParser {
return node.kind === ts.SyntaxKind.IndexedAccessType;
}

private createIndexedType(objectType: ts.TypeNode, context: Context, indexType: BaseType) {
private createIndexedType(objectType: ts.TypeNode, context: Context, indexType: BaseType): BaseType | undefined {
if (ts.isTypeReferenceNode(objectType) && indexType instanceof LiteralType) {
const declaration = this.typeChecker.getSymbolAtLocation(objectType.typeName)?.declarations?.[0];

Expand All @@ -40,17 +41,17 @@ export class IndexedAccessTypeNodeParser implements SubNodeParser {
return undefined;
}

public createType(node: ts.IndexedAccessTypeNode, context: Context): BaseType | undefined {
public createType(node: ts.IndexedAccessTypeNode, context: Context): BaseType {
const indexType = derefType(this.childNodeParser.createType(node.indexType, context));
const indexedType = indexType && this.createIndexedType(node.objectType, context, indexType);
const indexedType = this.createIndexedType(node.objectType, context, indexType);

if (indexedType) {
return indexedType;
}

const objectType = derefType(this.childNodeParser.createType(node.objectType, context));
if (objectType === undefined || indexType === undefined) {
return undefined;
if (objectType instanceof NeverType || indexType instanceof NeverType) {
return new NeverType();
}

const indexTypes = indexType instanceof UnionType ? indexType.getTypes() : [indexType];
Expand Down
2 changes: 1 addition & 1 deletion src/NodeParser/InferTypeNodeParser.ts
Expand Up @@ -11,7 +11,7 @@ export class InferTypeNodeParser implements SubNodeParser {
return node.kind === ts.SyntaxKind.InferType;
}

public createType(node: ts.InferTypeNode, _context: Context): BaseType | undefined {
public createType(node: ts.InferTypeNode, _context: Context): BaseType {
return new InferType(node.typeParameter.name.escapedText.toString());
}
}
20 changes: 7 additions & 13 deletions src/NodeParser/InterfaceAndClassNodeParser.ts
Expand Up @@ -3,12 +3,12 @@ import { Context, NodeParser } from "../NodeParser";
import { SubNodeParser } from "../SubNodeParser";
import { ArrayType } from "../Type/ArrayType";
import { BaseType } from "../Type/BaseType";
import { NeverType } from "../Type/NeverType";
import { ObjectProperty, ObjectType } from "../Type/ObjectType";
import { ReferenceType } from "../Type/ReferenceType";
import { isNodeHidden } from "../Utils/isHidden";
import { isPublic, isStatic } from "../Utils/modifiers";
import { getKey } from "../Utils/nodeKey";
import { notUndefined } from "../Utils/notUndefined";

export class InterfaceAndClassNodeParser implements SubNodeParser {
public constructor(
Expand All @@ -25,7 +25,7 @@ export class InterfaceAndClassNodeParser implements SubNodeParser {
node: ts.InterfaceDeclaration | ts.ClassDeclaration,
context: Context,
reference?: ReferenceType
): BaseType | undefined {
): BaseType {
if (node.typeParameters?.length) {
node.typeParameters.forEach((typeParam) => {
const nameSymbol = this.typeChecker.getSymbolAtLocation(typeParam.name)!;
Expand All @@ -47,7 +47,7 @@ export class InterfaceAndClassNodeParser implements SubNodeParser {
const properties = this.getProperties(node, context);

if (properties === undefined) {
return undefined;
return new NeverType();
}

const additionalProperties = this.getAdditionalProperties(node, context);
Expand All @@ -56,11 +56,7 @@ export class InterfaceAndClassNodeParser implements SubNodeParser {
if (properties.length === 0 && additionalProperties === false) {
const arrayItemType = this.getArrayItemType(node);
if (arrayItemType) {
const type = this.childNodeParser.createType(arrayItemType, context);
if (type === undefined) {
return undefined;
}
return new ArrayType(type);
return new ArrayType(this.childNodeParser.createType(arrayItemType, context));
}
}

Expand Down Expand Up @@ -99,9 +95,7 @@ export class InterfaceAndClassNodeParser implements SubNodeParser {
return node.heritageClauses.reduce(
(result: BaseType[], baseType) => [
...result,
...baseType.types
.map((expression) => this.childNodeParser.createType(expression, context))
.filter(notUndefined),
...baseType.types.map((expression) => this.childNodeParser.createType(expression, context)),
],
[]
);
Expand Down Expand Up @@ -135,10 +129,10 @@ export class InterfaceAndClassNodeParser implements SubNodeParser {
)
)
.filter((prop) => {
if (prop.isRequired() && prop.getType() === undefined) {
if (prop.isRequired() && prop.getType() instanceof NeverType) {
hasRequiredNever = true;
}
return prop.getType() !== undefined;
return !(prop.getType() instanceof NeverType);
});

if (hasRequiredNever) {
Expand Down

0 comments on commit 3ded513

Please sign in to comment.