UNPKG

inventoresed

Version:

Z-Wave driver written entirely in JavaScript/TypeScript

233 lines (210 loc) 5.91 kB
import * as tsutils from "tsutils/typeguard/3.0"; import ts from "typescript"; import type { VisitorContext } from "./visitor-context"; import * as VisitorUtils from "./visitor-utils"; function visitRegularObjectType() { return false; } function visitTupleObjectType() { return false; } function visitArrayObjectType() { return false; } function visitObjectType(type: ts.ObjectType, visitorContext: VisitorContext) { if (tsutils.isTupleType(type)) { // Tuple with finite length. return visitTupleObjectType(); } else if ( visitorContext.checker.getIndexTypeOfType(type, ts.IndexKind.Number) ) { // Index type is number -> array type. return visitArrayObjectType(); } else { // Index type is string -> regular object type. return visitRegularObjectType(); } } function visitUnionOrIntersectionType( type: ts.UnionOrIntersectionType, visitorContext: VisitorContext, ) { const numberTypes = type.types.map((type) => visitType(type, visitorContext), ); if (tsutils.isUnionType(type)) { if (numberTypes.some((numberType) => numberType === false)) { return false; } if (numberTypes.some((numberType) => numberType === true)) { return true; } const numbers: Set<number> = new Set(); for (const numberType of numberTypes) { for (const value of numberType as Set<number>) { numbers.add(value); } } return numbers; } else { const numbers: Set<number> = new Set(); for (const numberType of numberTypes) { if (typeof numberType !== "boolean") { for (const value of numberType) { numbers.add(value); } } } if (numbers.size === 1) { return numbers; } if (numbers.size > 1) { return false; } if (numberTypes.some((numberType) => numberType === true)) { return true; } return false; } } function visitIndexType(): boolean { // TODO: implement a visitor that checks if the index type is an array/tuple, then this can be a number. throw new Error("Not yet implemented."); } function visitNonPrimitiveType() { return false; } function visitLiteralType(type: ts.LiteralType) { if (typeof type.value === "string") { return false; } else if (typeof type.value === "number") { return new Set([type.value]); } else { throw new Error("Type value is expected to be a string or number."); } } function visitTypeReference( type: ts.TypeReference, visitorContext: VisitorContext, ) { const mapping: Map<ts.Type, ts.Type> = VisitorUtils.getTypeReferenceMapping( type, visitorContext, ); const previousTypeReference = visitorContext.previousTypeReference; visitorContext.typeMapperStack.push(mapping); visitorContext.previousTypeReference = type; const result = visitType(type.target, visitorContext); visitorContext.previousTypeReference = previousTypeReference; visitorContext.typeMapperStack.pop(); return result; } function visitTypeParameter(type: ts.Type, visitorContext: VisitorContext) { const mappedType = VisitorUtils.getResolvedTypeParameter( type, visitorContext, ); if (mappedType === undefined) { throw new Error("Unbound type parameter, missing type node."); } return visitType(mappedType, visitorContext); } function visitBigInt() { return false; } function visitBoolean() { return false; } function visitString() { return false; } function visitBooleanLiteral() { return false; } function visitNumber() { return true; } function visitUndefined() { return false; } function visitNull() { return false; } function visitNever() { return false; } function visitUnknown() { return false; } function visitAny() { return true; } export function visitType( type: ts.Type, visitorContext: VisitorContext, ): Set<number> | boolean { if ((ts.TypeFlags.Any & type.flags) !== 0) { // Any return visitAny(); } else if ((ts.TypeFlags.Unknown & type.flags) !== 0) { // Unknown return visitUnknown(); } else if ((ts.TypeFlags.Never & type.flags) !== 0) { // Never return visitNever(); } else if ((ts.TypeFlags.Null & type.flags) !== 0) { // Null return visitNull(); } else if ((ts.TypeFlags.Undefined & type.flags) !== 0) { // Undefined return visitUndefined(); } else if ((ts.TypeFlags.Number & type.flags) !== 0) { // Number return visitNumber(); } else if (VisitorUtils.isBigIntType(type)) { // BigInt return visitBigInt(); } else if ((ts.TypeFlags.Boolean & type.flags) !== 0) { // Boolean return visitBoolean(); } else if ((ts.TypeFlags.String & type.flags) !== 0) { // String return visitString(); } else if ((ts.TypeFlags.BooleanLiteral & type.flags) !== 0) { // Boolean literal (true/false) return visitBooleanLiteral(); } else if ( tsutils.isTypeReference(type) && visitorContext.previousTypeReference !== type ) { // Type references. return visitTypeReference(type, visitorContext); } else if ((ts.TypeFlags.TypeParameter & type.flags) !== 0) { // Type parameter return visitTypeParameter(type, visitorContext); } else if (tsutils.isObjectType(type)) { // Object type (including interfaces, arrays, tuples) return visitObjectType(type, visitorContext); } else if (tsutils.isLiteralType(type)) { // Literal string/number types ('foo') return visitLiteralType(type); } else if (tsutils.isUnionOrIntersectionType(type)) { // Union or intersection type (| or &) return visitUnionOrIntersectionType(type, visitorContext); } else if ((ts.TypeFlags.NonPrimitive & type.flags) !== 0) { // Non-primitive such as object return visitNonPrimitiveType(); } else if ((ts.TypeFlags.Index & type.flags) !== 0) { // Index type: keyof T return visitIndexType(); } else if (tsutils.isIndexedAccessType(type)) { // Indexed access type: T[U] // return visitIndexedAccessType(type, visitorContext); // TODO: throw new Error("Not yet implemented."); } else { throw new Error( `Could not generate type-check; unsupported type with flags: ${type.flags}`, ); } }