Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: consistently use NeverType #1367

Merged
merged 2 commits into from Aug 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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