@sourcegraph/eslint-plugin-sourcegraph
Version:
Eslint plugin Sourcegraph
300 lines • 10.9 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getContextualType = exports.isUnsafeAssignment = exports.isAnyOrAnyArrayTypeDiscriminated = exports.isTypeUnknownArrayType = exports.isTypeAnyArrayType = exports.isTypeAnyType = exports.isTypeUnknownType = exports.getTypeArguments = exports.getEqualsKind = exports.getTokenAtPosition = exports.getSourceFileOfNode = exports.typeIsOrHasBaseType = exports.isTypeFlagSet = exports.getTypeFlags = exports.getDeclaration = exports.isNullableType = exports.getConstrainedTypeAtLocation = exports.getTypeName = exports.containsAllTypesByName = exports.isTypeArrayTypeOrUnionOfArrayTypes = void 0;
const tslib_1 = require("tslib");
const experimental_utils_1 = require("@typescript-eslint/experimental-utils");
const debug_1 = (0, tslib_1.__importDefault)(require("debug"));
const tsutils_1 = require("tsutils");
const ts = (0, tslib_1.__importStar)(require("typescript"));
const log = (0, debug_1.default)('typescript-eslint:eslint-plugin:utils:types');
function isTypeArrayTypeOrUnionOfArrayTypes(type, checker) {
for (const t of (0, tsutils_1.unionTypeParts)(type)) {
if (!checker.isArrayType(t)) {
return false;
}
}
return true;
}
exports.isTypeArrayTypeOrUnionOfArrayTypes = isTypeArrayTypeOrUnionOfArrayTypes;
function containsAllTypesByName(type, allowAny, allowedNames) {
if (isTypeFlagSet(type, ts.TypeFlags.Any | ts.TypeFlags.Unknown)) {
return !allowAny;
}
if ((0, tsutils_1.isTypeReference)(type)) {
type = type.target;
}
const symbol = type.getSymbol();
if (symbol && allowedNames.has(symbol.name)) {
return true;
}
if ((0, tsutils_1.isUnionOrIntersectionType)(type)) {
return type.types.every(t => {
return containsAllTypesByName(t, allowAny, allowedNames);
});
}
const bases = type.getBaseTypes();
return (typeof bases !== 'undefined' &&
bases.length > 0 &&
bases.every(t => {
return containsAllTypesByName(t, allowAny, allowedNames);
}));
}
exports.containsAllTypesByName = containsAllTypesByName;
function getTypeName(typeChecker, type) {
if ((type.flags & ts.TypeFlags.StringLike) !== 0) {
return 'string';
}
if ((type.flags & ts.TypeFlags.TypeParameter) !== 0) {
const symbol = type.getSymbol();
const decls = symbol?.getDeclarations();
const typeParameterDecl = decls?.[0];
if (ts.isTypeParameterDeclaration(typeParameterDecl) && typeParameterDecl.constraint) {
return getTypeName(typeChecker, typeChecker.getTypeFromTypeNode(typeParameterDecl.constraint));
}
}
if (type.isUnion() &&
type.types
.map(value => {
return getTypeName(typeChecker, value);
})
.every(t => {
return t === 'string';
})) {
return 'string';
}
if (type.isIntersection() &&
type.types
.map(value => {
return getTypeName(typeChecker, value);
})
.some(t => {
return t === 'string';
})) {
return 'string';
}
return typeChecker.typeToString(type);
}
exports.getTypeName = getTypeName;
function getConstrainedTypeAtLocation(checker, node) {
const nodeType = checker.getTypeAtLocation(node);
const constrained = checker.getBaseConstraintOfType(nodeType);
return constrained ?? nodeType;
}
exports.getConstrainedTypeAtLocation = getConstrainedTypeAtLocation;
function isNullableType(type, { isReceiver = false, allowUndefined = true } = {}) {
const flags = getTypeFlags(type);
if (isReceiver && flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) {
return true;
}
if (allowUndefined) {
return (flags & (ts.TypeFlags.Null | ts.TypeFlags.Undefined)) !== 0;
}
return (flags & ts.TypeFlags.Null) !== 0;
}
exports.isNullableType = isNullableType;
function getDeclaration(checker, node) {
const symbol = checker.getSymbolAtLocation(node);
if (!symbol) {
return null;
}
const declarations = symbol.getDeclarations();
return declarations?.[0] ?? null;
}
exports.getDeclaration = getDeclaration;
function getTypeFlags(type) {
let flags = 0;
for (const t of (0, tsutils_1.unionTypeParts)(type)) {
flags |= t.flags;
}
return flags;
}
exports.getTypeFlags = getTypeFlags;
function isTypeFlagSet(type, flagsToCheck, isReceiver) {
const flags = getTypeFlags(type);
if (isReceiver && flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) {
return true;
}
return (flags & flagsToCheck) !== 0;
}
exports.isTypeFlagSet = isTypeFlagSet;
function typeIsOrHasBaseType(type, parentType) {
const parentSymbol = parentType.getSymbol();
if (!type.getSymbol() || !parentSymbol) {
return false;
}
const typeAndBaseTypes = [type];
const ancestorTypes = type.getBaseTypes();
if (ancestorTypes) {
typeAndBaseTypes.push(...ancestorTypes);
}
for (const baseType of typeAndBaseTypes) {
const baseSymbol = baseType.getSymbol();
if (baseSymbol && baseSymbol.name === parentSymbol.name) {
return true;
}
}
return false;
}
exports.typeIsOrHasBaseType = typeIsOrHasBaseType;
function getSourceFileOfNode(node) {
while (node && node.kind !== ts.SyntaxKind.SourceFile) {
node = node.parent;
}
return node;
}
exports.getSourceFileOfNode = getSourceFileOfNode;
function getTokenAtPosition(sourceFile, position) {
const queue = [sourceFile];
let current;
while (queue.length > 0) {
current = queue.shift();
for (const child of current.getChildren(sourceFile)) {
const start = child.getFullStart();
if (start > position) {
return current;
}
const end = child.getEnd();
if (position < end || (position === end && child.kind === ts.SyntaxKind.EndOfFileToken)) {
queue.push(child);
break;
}
}
}
return current;
}
exports.getTokenAtPosition = getTokenAtPosition;
function getEqualsKind(operator) {
switch (operator) {
case '==':
return {
isPositive: true,
isStrict: false,
};
case '===':
return {
isPositive: true,
isStrict: true,
};
case '!=':
return {
isPositive: false,
isStrict: false,
};
case '!==':
return {
isPositive: false,
isStrict: true,
};
default:
return undefined;
}
}
exports.getEqualsKind = getEqualsKind;
function getTypeArguments(type, checker) {
if (checker.getTypeArguments) {
return checker.getTypeArguments(type);
}
return type.typeArguments ?? [];
}
exports.getTypeArguments = getTypeArguments;
function isTypeUnknownType(type) {
return isTypeFlagSet(type, ts.TypeFlags.Unknown);
}
exports.isTypeUnknownType = isTypeUnknownType;
function isTypeAnyType(type) {
if (isTypeFlagSet(type, ts.TypeFlags.Any)) {
if (type.intrinsicName === 'error') {
log('Found an "error" any type');
}
return true;
}
return false;
}
exports.isTypeAnyType = isTypeAnyType;
function isTypeAnyArrayType(type, checker) {
return (checker.isArrayType(type) &&
isTypeAnyType(getTypeArguments(type, checker)[0]));
}
exports.isTypeAnyArrayType = isTypeAnyArrayType;
function isTypeUnknownArrayType(type, checker) {
return (checker.isArrayType(type) &&
isTypeUnknownType(getTypeArguments(type, checker)[0]));
}
exports.isTypeUnknownArrayType = isTypeUnknownArrayType;
function isAnyOrAnyArrayTypeDiscriminated(node, checker) {
const type = checker.getTypeAtLocation(node);
if (isTypeAnyType(type)) {
return 0;
}
if (isTypeAnyArrayType(type, checker)) {
return 1;
}
return 2;
}
exports.isAnyOrAnyArrayTypeDiscriminated = isAnyOrAnyArrayTypeDiscriminated;
function isUnsafeAssignment(type, receiver, checker, senderNode) {
if (isTypeAnyType(type)) {
if (isTypeUnknownType(receiver)) {
return false;
}
if (!isTypeAnyType(receiver)) {
return { sender: type, receiver };
}
}
if ((0, tsutils_1.isTypeReference)(type) && (0, tsutils_1.isTypeReference)(receiver)) {
if (type.target !== receiver.target) {
return false;
}
if (senderNode?.type === experimental_utils_1.AST_NODE_TYPES.NewExpression &&
senderNode.callee.type === experimental_utils_1.AST_NODE_TYPES.Identifier &&
senderNode.callee.name === 'Map' &&
senderNode.arguments.length === 0 &&
(senderNode.typeParameters === null || senderNode.typeParameters === undefined)) {
return false;
}
const typeArguments = type.typeArguments ?? [];
const receiverTypeArguments = receiver.typeArguments ?? [];
for (let index = 0; index < typeArguments.length; index += 1) {
const argument = typeArguments[index];
const receiverArgument = receiverTypeArguments[index];
const unsafe = isUnsafeAssignment(argument, receiverArgument, checker, senderNode);
if (unsafe) {
return { sender: type, receiver };
}
}
return false;
}
return false;
}
exports.isUnsafeAssignment = isUnsafeAssignment;
function getContextualType(checker, node) {
const parent = node.parent;
if (!parent) {
return;
}
if ((0, tsutils_1.isCallExpression)(parent) || (0, tsutils_1.isNewExpression)(parent)) {
if (node === parent.expression) {
return;
}
}
else if ((0, tsutils_1.isVariableDeclaration)(parent) || (0, tsutils_1.isPropertyDeclaration)(parent) || (0, tsutils_1.isParameterDeclaration)(parent)) {
return parent.type ? checker.getTypeFromTypeNode(parent.type) : undefined;
}
else if ((0, tsutils_1.isJsxExpression)(parent)) {
return checker.getContextualType(parent);
}
else if ((0, tsutils_1.isPropertyAssignment)(parent) && (0, tsutils_1.isIdentifier)(node)) {
return checker.getContextualType(node);
}
else if ((0, tsutils_1.isBinaryExpression)(parent) &&
parent.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
parent.right === node) {
return checker.getTypeAtLocation(parent.left);
}
else if (![ts.SyntaxKind.TemplateSpan, ts.SyntaxKind.JsxExpression].includes(parent.kind)) {
return;
}
return checker.getContextualType(node);
}
exports.getContextualType = getContextualType;
//# sourceMappingURL=types.js.map