UNPKG

eslint-plugin-jest

Version:
104 lines (100 loc) 3.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.isBuiltinSymbolLike = isBuiltinSymbolLike; exports.isThenableType = isThenableType; let SymbolFlags; let TypeFlags; function loadTypeScriptFlags() { // eslint-disable-next-line @typescript-eslint/no-require-imports ({ TypeFlags, SymbolFlags } = require('typescript')); } function isSymbolFromDefaultLibrary(program, symbol) { /* istanbul ignore next */ const declarations = symbol.getDeclarations() ?? []; for (const declaration of declarations) { const sourceFile = declaration.getSourceFile(); /* istanbul ignore else */ if (program.isSourceFileDefaultLibrary(sourceFile)) { return true; } } /* istanbul ignore next */ return false; } function isBuiltinSymbolLike(program, type, symbolName) { loadTypeScriptFlags(); return isBuiltinSymbolLikeRecurser(program, type, subType => { const symbol = subType.getSymbol(); if (!symbol) { return false; } const actualSymbolName = symbol.getName(); if (actualSymbolName === symbolName && isSymbolFromDefaultLibrary(program, symbol)) { return true; } return null; }); } /** * Checks if the given type is thenable: it has a `then` method that accepts * both a fulfillment and a rejection callback, mirroring the check performed * by typescript-eslint's `no-floating-promises` rule. */ function isThenableType(program, node, type) { loadTypeScriptFlags(); const checker = program.getTypeChecker(); return isBuiltinSymbolLikeRecurser(program, type, subType => { const then = subType.getProperty('then'); if (!then) { return false; } return unionParts(checker.getTypeOfSymbolAtLocation(then, node)).some(thenType => thenType.getCallSignatures().some(signature => { const [onFulfilled, onRejected] = signature.getParameters(); return onFulfilled !== undefined && onRejected !== undefined && isFunctionParam(checker, node, onFulfilled) && isFunctionParam(checker, node, onRejected); })); }); } function isFunctionParam(checker, node, param) { const paramType = checker.getApparentType(checker.getTypeOfSymbolAtLocation(param, node)); return unionParts(paramType).some(subType => subType.getCallSignatures().length > 0); } function unionParts(type) { return type.isUnion() ? type.types : [type]; } function isBuiltinSymbolLikeRecurser(program, type, predicate) { if (type.isIntersection()) { return type.types.some(t => isBuiltinSymbolLikeRecurser(program, t, predicate)); } if (type.isUnion()) { return type.types.every(t => isBuiltinSymbolLikeRecurser(program, t, predicate)); } if (isTypeParameter(type)) { const t = type.getConstraint(); if (t) { return isBuiltinSymbolLikeRecurser(program, t, predicate); } return false; } const predicateResult = predicate(type); if (typeof predicateResult === 'boolean') { return predicateResult; } const symbol = type.getSymbol(); if (symbol && symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { const checker = program.getTypeChecker(); for (const baseType of checker.getBaseTypes(type)) { if (isBuiltinSymbolLikeRecurser(program, baseType, predicate)) { return true; } } } return false; } function isTypeParameter(type) { return (type.flags & TypeFlags.TypeParameter) !== 0; }