"use strict"; | |
Object.defineProperty(exports, "__esModule", { value: true }); | |
exports.getBaseClassMemberOfClassElement = exports.getIteratorYieldResultFromIteratorResult = exports.getInstanceTypeOfClassLikeDeclaration = exports.getConstructorTypeOfClassLikeDeclaration = exports.getSymbolOfClassLikeDeclaration = exports.getPropertyNameFromType = exports.symbolHasReadonlyDeclaration = exports.isPropertyReadonlyInType = exports.getWellKnownSymbolPropertyOfType = exports.getPropertyOfType = exports.isBooleanLiteralType = exports.isFalsyType = exports.isThenableType = exports.someTypePart = exports.intersectionTypeParts = exports.unionTypeParts = exports.getCallSignaturesOfType = exports.isTypeAssignableToString = exports.isTypeAssignableToNumber = exports.isOptionalChainingUndefinedMarkerType = exports.removeOptionalChainingUndefinedMarkerType = exports.removeOptionalityFromType = exports.isEmptyObjectType = void 0; | |
const ts = require("typescript"); | |
const type_1 = require("../typeguard/type"); | |
const util_1 = require("./util"); | |
const node_1 = require("../typeguard/node"); | |
function isEmptyObjectType(type) { | |
if (type_1.isObjectType(type) && | |
type.objectFlags & ts.ObjectFlags.Anonymous && | |
type.getProperties().length === 0 && | |
type.getCallSignatures().length === 0 && | |
type.getConstructSignatures().length === 0 && | |
type.getStringIndexType() === undefined && | |
type.getNumberIndexType() === undefined) { | |
const baseTypes = type.getBaseTypes(); | |
return baseTypes === undefined || baseTypes.every(isEmptyObjectType); | |
} | |
return false; | |
} | |
exports.isEmptyObjectType = isEmptyObjectType; | |
function removeOptionalityFromType(checker, type) { | |
if (!containsTypeWithFlag(type, ts.TypeFlags.Undefined)) | |
return type; | |
const allowsNull = containsTypeWithFlag(type, ts.TypeFlags.Null); | |
type = checker.getNonNullableType(type); | |
return allowsNull ? checker.getNullableType(type, ts.TypeFlags.Null) : type; | |
} | |
exports.removeOptionalityFromType = removeOptionalityFromType; | |
function containsTypeWithFlag(type, flag) { | |
for (const t of unionTypeParts(type)) | |
if (util_1.isTypeFlagSet(t, flag)) | |
return true; | |
return false; | |
} | |
function removeOptionalChainingUndefinedMarkerType(checker, type) { | |
if (!type_1.isUnionType(type)) | |
return isOptionalChainingUndefinedMarkerType(checker, type) ? type.getNonNullableType() : type; | |
let flags = 0; | |
let containsUndefinedMarker = false; | |
for (const t of type.types) { | |
if (isOptionalChainingUndefinedMarkerType(checker, t)) { | |
containsUndefinedMarker = true; | |
} | |
else { | |
flags |= t.flags; | |
} | |
} | |
return containsUndefinedMarker | |
? checker.getNullableType(type.getNonNullableType(), flags) | |
: type; | |
} | |
exports.removeOptionalChainingUndefinedMarkerType = removeOptionalChainingUndefinedMarkerType; | |
function isOptionalChainingUndefinedMarkerType(checker, t) { | |
return util_1.isTypeFlagSet(t, ts.TypeFlags.Undefined) && checker.getNullableType(t.getNonNullableType(), ts.TypeFlags.Undefined) !== t; | |
} | |
exports.isOptionalChainingUndefinedMarkerType = isOptionalChainingUndefinedMarkerType; | |
function isTypeAssignableToNumber(checker, type) { | |
return isTypeAssignableTo(checker, type, ts.TypeFlags.NumberLike); | |
} | |
exports.isTypeAssignableToNumber = isTypeAssignableToNumber; | |
function isTypeAssignableToString(checker, type) { | |
return isTypeAssignableTo(checker, type, ts.TypeFlags.StringLike); | |
} | |
exports.isTypeAssignableToString = isTypeAssignableToString; | |
function isTypeAssignableTo(checker, type, flags) { | |
flags |= ts.TypeFlags.Any; | |
let typeParametersSeen; | |
return (function check(t) { | |
if (type_1.isTypeParameter(t) && t.symbol !== undefined && t.symbol.declarations !== undefined) { | |
if (typeParametersSeen === undefined) { | |
typeParametersSeen = new Set([t]); | |
} | |
else if (!typeParametersSeen.has(t)) { | |
typeParametersSeen.add(t); | |
} | |
else { | |
return false; | |
} | |
const declaration = t.symbol.declarations[0]; | |
if (declaration.constraint === undefined) | |
return true; // TODO really? | |
return check(checker.getTypeFromTypeNode(declaration.constraint)); | |
} | |
if (type_1.isUnionType(t)) | |
return t.types.every(check); | |
if (type_1.isIntersectionType(t)) | |
return t.types.some(check); | |
return util_1.isTypeFlagSet(t, flags); | |
})(type); | |
} | |
function getCallSignaturesOfType(type) { | |
if (type_1.isUnionType(type)) { | |
const signatures = []; | |
for (const t of type.types) | |
signatures.push(...getCallSignaturesOfType(t)); | |
return signatures; | |
} | |
if (type_1.isIntersectionType(type)) { | |
let signatures; | |
for (const t of type.types) { | |
const sig = getCallSignaturesOfType(t); | |
if (sig.length !== 0) { | |
if (signatures !== undefined) | |
return []; // if more than one type of the intersection has call signatures, none of them is useful for inference | |
signatures = sig; | |
} | |
} | |
return signatures === undefined ? [] : signatures; | |
} | |
return type.getCallSignatures(); | |
} | |
exports.getCallSignaturesOfType = getCallSignaturesOfType; | |
/** Returns all types of a union type or an array containing `type` itself if it's no union type. */ | |
function unionTypeParts(type) { | |
return type_1.isUnionType(type) ? type.types : [type]; | |
} | |
exports.unionTypeParts = unionTypeParts; | |
/** Returns all types of a intersection type or an array containing `type` itself if it's no intersection type. */ | |
function intersectionTypeParts(type) { | |
return type_1.isIntersectionType(type) ? type.types : [type]; | |
} | |
exports.intersectionTypeParts = intersectionTypeParts; | |
function someTypePart(type, predicate, cb) { | |
return predicate(type) ? type.types.some(cb) : cb(type); | |
} | |
exports.someTypePart = someTypePart; | |
function isThenableType(checker, node, type = checker.getTypeAtLocation(node)) { | |
for (const ty of unionTypeParts(checker.getApparentType(type))) { | |
const then = ty.getProperty('then'); | |
if (then === undefined) | |
continue; | |
const thenType = checker.getTypeOfSymbolAtLocation(then, node); | |
for (const t of unionTypeParts(thenType)) | |
for (const signature of t.getCallSignatures()) | |
if (signature.parameters.length !== 0 && isCallback(checker, signature.parameters[0], node)) | |
return true; | |
} | |
return false; | |
} | |
exports.isThenableType = isThenableType; | |
function isCallback(checker, param, node) { | |
let type = checker.getApparentType(checker.getTypeOfSymbolAtLocation(param, node)); | |
if (param.valueDeclaration.dotDotDotToken) { | |
// unwrap array type of rest parameter | |
type = type.getNumberIndexType(); | |
if (type === undefined) | |
return false; | |
} | |
for (const t of unionTypeParts(type)) | |
if (t.getCallSignatures().length !== 0) | |
return true; | |
return false; | |
} | |
/** Determine if a type is definitely falsy. This function doesn't unwrap union types. */ | |
function isFalsyType(type) { | |
if (type.flags & (ts.TypeFlags.Undefined | ts.TypeFlags.Null | ts.TypeFlags.Void)) | |
return true; | |
if (type_1.isLiteralType(type)) | |
return !type.value; | |
return isBooleanLiteralType(type, false); | |
} | |
exports.isFalsyType = isFalsyType; | |
/** Determines whether the given type is a boolean literal type and matches the given boolean literal (true or false). */ | |
function isBooleanLiteralType(type, literal) { | |
return util_1.isTypeFlagSet(type, ts.TypeFlags.BooleanLiteral) && | |
type.intrinsicName === (literal ? 'true' : 'false'); | |
} | |
exports.isBooleanLiteralType = isBooleanLiteralType; | |
function getPropertyOfType(type, name) { | |
if (!name.startsWith('__')) | |
return type.getProperty(name); | |
return type.getProperties().find((s) => s.escapedName === name); | |
} | |
exports.getPropertyOfType = getPropertyOfType; | |
function getWellKnownSymbolPropertyOfType(type, wellKnownSymbolName, checker) { | |
const prefix = '__@' + wellKnownSymbolName; | |
for (const prop of type.getProperties()) { | |
if (!prop.name.startsWith(prefix)) | |
continue; | |
const globalSymbol = checker.getApparentType(checker.getTypeAtLocation(prop.valueDeclaration.name.expression)).symbol; | |
if (prop.escapedName === getPropertyNameOfWellKnownSymbol(checker, globalSymbol, wellKnownSymbolName)) | |
return prop; | |
} | |
return; | |
} | |
exports.getWellKnownSymbolPropertyOfType = getWellKnownSymbolPropertyOfType; | |
function getPropertyNameOfWellKnownSymbol(checker, symbolConstructor, symbolName) { | |
const knownSymbol = symbolConstructor && | |
checker.getTypeOfSymbolAtLocation(symbolConstructor, symbolConstructor.valueDeclaration).getProperty(symbolName); | |
const knownSymbolType = knownSymbol && checker.getTypeOfSymbolAtLocation(knownSymbol, knownSymbol.valueDeclaration); | |
if (knownSymbolType && type_1.isUniqueESSymbolType(knownSymbolType)) | |
return knownSymbolType.escapedName; | |
return ('__@' + symbolName); | |
} | |
/** Determines if writing to a certain property of a given type is allowed. */ | |
function isPropertyReadonlyInType(type, name, checker) { | |
let seenProperty = false; | |
let seenReadonlySignature = false; | |
for (const t of unionTypeParts(type)) { | |
if (getPropertyOfType(t, name) === undefined) { | |
// property is not present in this part of the union -> check for readonly index signature | |
const index = (util_1.isNumericPropertyName(name) ? checker.getIndexInfoOfType(t, ts.IndexKind.Number) : undefined) || | |
checker.getIndexInfoOfType(t, ts.IndexKind.String); | |
if (index !== undefined && index.isReadonly) { | |
if (seenProperty) | |
return true; | |
seenReadonlySignature = true; | |
} | |
} | |
else if (seenReadonlySignature || isReadonlyPropertyIntersection(t, name, checker)) { | |
return true; | |
} | |
else { | |
seenProperty = true; | |
} | |
} | |
return false; | |
} | |
exports.isPropertyReadonlyInType = isPropertyReadonlyInType; | |
function isReadonlyPropertyIntersection(type, name, checker) { | |
return someTypePart(type, type_1.isIntersectionType, (t) => { | |
const prop = getPropertyOfType(t, name); | |
if (prop === undefined) | |
return false; | |
if (prop.flags & ts.SymbolFlags.Transient) { | |
if (/^(?:[1-9]\d*|0)$/.test(name) && type_1.isTupleTypeReference(t)) | |
return t.target.readonly; | |
switch (isReadonlyPropertyFromMappedType(t, name, checker)) { | |
case true: | |
return true; | |
case false: | |
return false; | |
default: | |
// `undefined` falls through | |
} | |
} | |
return ( | |
// members of namespace import | |
util_1.isSymbolFlagSet(prop, ts.SymbolFlags.ValueModule) || | |
// we unwrapped every mapped type, now we can check the actual declarations | |
symbolHasReadonlyDeclaration(prop, checker)); | |
}); | |
} | |
function isReadonlyPropertyFromMappedType(type, name, checker) { | |
if (!type_1.isObjectType(type) || !util_1.isObjectFlagSet(type, ts.ObjectFlags.Mapped)) | |
return; | |
const declaration = type.symbol.declarations[0]; | |
// well-known symbols are not affected by mapped types | |
if (declaration.readonlyToken !== undefined && !/^__@[^@]+$/.test(name)) | |
return declaration.readonlyToken.kind !== ts.SyntaxKind.MinusToken; | |
return isPropertyReadonlyInType(type.modifiersType, name, checker); | |
} | |
function symbolHasReadonlyDeclaration(symbol, checker) { | |
return (symbol.flags & ts.SymbolFlags.Accessor) === ts.SymbolFlags.GetAccessor || | |
symbol.declarations !== undefined && | |
symbol.declarations.some((node) => util_1.isModifierFlagSet(node, ts.ModifierFlags.Readonly) || | |
node_1.isVariableDeclaration(node) && util_1.isNodeFlagSet(node.parent, ts.NodeFlags.Const) || | |
node_1.isCallExpression(node) && util_1.isReadonlyAssignmentDeclaration(node, checker) || | |
node_1.isEnumMember(node) || | |
(node_1.isPropertyAssignment(node) || node_1.isShorthandPropertyAssignment(node)) && util_1.isInConstContext(node.parent)); | |
} | |
exports.symbolHasReadonlyDeclaration = symbolHasReadonlyDeclaration; | |
/** Returns the the literal name or unique symbol name from a given type. Doesn't unwrap union types. */ | |
function getPropertyNameFromType(type) { | |
// string or number literal. bigint is intentionally excluded | |
if (type.flags & (ts.TypeFlags.StringLiteral | ts.TypeFlags.NumberLiteral)) { | |
const value = String(type.value); | |
return { displayName: value, symbolName: ts.escapeLeadingUnderscores(value) }; | |
} | |
if (type_1.isUniqueESSymbolType(type)) | |
return { | |
displayName: `[${type.symbol | |
? `${isKnownSymbol(type.symbol) ? 'Symbol.' : ''}${type.symbol.name}` | |
: type.escapedName.replace(/^__@|@\d+$/g, '')}]`, | |
symbolName: type.escapedName, | |
}; | |
} | |
exports.getPropertyNameFromType = getPropertyNameFromType; | |
function isKnownSymbol(symbol) { | |
return util_1.isSymbolFlagSet(symbol, ts.SymbolFlags.Property) && | |
symbol.valueDeclaration !== undefined && | |
node_1.isInterfaceDeclaration(symbol.valueDeclaration.parent) && | |
symbol.valueDeclaration.parent.name.text === 'SymbolConstructor' && | |
isGlobalDeclaration(symbol.valueDeclaration.parent); | |
} | |
function isGlobalDeclaration(node) { | |
return util_1.isNodeFlagSet(node.parent, ts.NodeFlags.GlobalAugmentation) || node_1.isSourceFile(node.parent) && !ts.isExternalModule(node.parent); | |
} | |
function getSymbolOfClassLikeDeclaration(node, checker) { | |
var _a; | |
return checker.getSymbolAtLocation((_a = node.name) !== null && _a !== void 0 ? _a : util_1.getChildOfKind(node, ts.SyntaxKind.ClassKeyword)); | |
} | |
exports.getSymbolOfClassLikeDeclaration = getSymbolOfClassLikeDeclaration; | |
function getConstructorTypeOfClassLikeDeclaration(node, checker) { | |
return node.kind === ts.SyntaxKind.ClassExpression | |
? checker.getTypeAtLocation(node) | |
: checker.getTypeOfSymbolAtLocation(getSymbolOfClassLikeDeclaration(node, checker), node); | |
} | |
exports.getConstructorTypeOfClassLikeDeclaration = getConstructorTypeOfClassLikeDeclaration; | |
function getInstanceTypeOfClassLikeDeclaration(node, checker) { | |
return node.kind === ts.SyntaxKind.ClassDeclaration | |
? checker.getTypeAtLocation(node) | |
: checker.getDeclaredTypeOfSymbol(getSymbolOfClassLikeDeclaration(node, checker)); | |
} | |
exports.getInstanceTypeOfClassLikeDeclaration = getInstanceTypeOfClassLikeDeclaration; | |
function getIteratorYieldResultFromIteratorResult(type, node, checker) { | |
return type_1.isUnionType(type) && type.types.find((t) => { | |
const done = t.getProperty('done'); | |
return done !== undefined && | |
isBooleanLiteralType(removeOptionalityFromType(checker, checker.getTypeOfSymbolAtLocation(done, node)), false); | |
}) || type; | |
} | |
exports.getIteratorYieldResultFromIteratorResult = getIteratorYieldResultFromIteratorResult; | |
/** Lookup the declaration of a class member in the super class. */ | |
function getBaseClassMemberOfClassElement(node, checker) { | |
if (!node_1.isClassLikeDeclaration(node.parent)) | |
return; | |
const base = util_1.getBaseOfClassLikeExpression(node.parent); | |
if (base === undefined) | |
return; | |
const name = util_1.getSingleLateBoundPropertyNameOfPropertyName(node.name, checker); | |
if (name === undefined) | |
return; | |
const baseType = checker.getTypeAtLocation(util_1.hasModifier(node.modifiers, ts.SyntaxKind.StaticKeyword) | |
? base.expression | |
: base); | |
return getPropertyOfType(baseType, name.symbolName); | |
} | |
exports.getBaseClassMemberOfClassElement = getBaseClassMemberOfClassElement; | |
//# sourceMappingURL=type.js.map |