eslint-plugin-unicorn
Version:
More than 300 powerful ESLint rules
90 lines (69 loc) • 3.19 kB
JavaScript
const unknownTypeNames = new Set(['any', 'error', 'unknown']);
const nullishTypeNames = new Set(['null', 'undefined']);
// `TypeFlags` bit values from TypeScript.
const templateLiteralTypeFlag = 4_194_304; // TypeFlags.TemplateLiteral
const uniqueSymbolTypeFlag = 16_384; // TypeFlags.UniqueESSymbol
const stringMappingTypeFlag = 8_388_608; // TypeFlags.StringMapping
const typeParameterTypeFlag = 524_288; // TypeFlags.TypeParameter
// `SymbolFlags` bit values from TypeScript.
const constEnumSymbolFlag = 128; // SymbolFlags.ConstEnum
const aliasSymbolFlag = 2_097_152; // SymbolFlags.Alias
// Test a single flag bit. Uses modulo rather than bitwise `&`, which truncates to 32 bits, to stay correct if the flags ever grow past that.
const hasFlag = (flags, flag) => (flags % (flag * 2)) >= flag;
const typeHasFlag = (type, flag) => hasFlag(type.flags, flag);
const isConstEnumSymbol = symbol => hasFlag(symbol.flags, constEnumSymbolFlag);
const isAliasSymbol = symbol => hasFlag(symbol.flags, aliasSymbolFlag);
const isTemplateLiteralType = type => typeHasFlag(type, templateLiteralTypeFlag);
const isUniqueSymbolType = type => typeHasFlag(type, uniqueSymbolTypeFlag);
const isStringMappingType = type => typeHasFlag(type, stringMappingTypeFlag);
const isTypeParameterType = type =>
type.isTypeParameter?.() || typeHasFlag(type, typeParameterTypeFlag);
const getTypeSymbol = type => type.getSymbol() ?? type.aliasSymbol;
const isDefaultLibrarySymbol = (symbol, program) =>
symbol?.declarations?.some(declaration => program.isSourceFileDefaultLibrary(declaration.getSourceFile())) ?? false;
const isUnknownType = type => unknownTypeNames.has(type.intrinsicName);
const isNullishType = type => nullishTypeNames.has(type.intrinsicName);
function getBaseTypes(type, checker) {
try {
return checker.getBaseTypes(type);
} catch {
return [];
}
}
/**
Call `callback` with the TypeScript type information of `node`: `{type, checker, program}`.
Returns what `callback` returns, or `undefined` when the file has no full type information (no `parserServices.program`) or TypeScript throws. `callback` runs inside the `try`, so it can use the checker without its own error handling.
@param {import('estree').Node} node
@param {import('eslint').Rule.RuleContext} context
@param {(typeInformation: {type: any, checker: any, program: any}) => any} callback
*/
function withTypeInformation(node, context, callback) {
const {parserServices} = context.sourceCode;
if (!parserServices?.program) {
return;
}
try {
const {program} = parserServices;
return callback({
type: parserServices.getTypeAtLocation(node),
checker: program.getTypeChecker(),
program,
});
} catch {
// `getTypeAtLocation()` throws for a node that the TypeScript program does not map, and the checker can throw while resolving incomplete projects. Type information is best-effort, so callers fall back to their syntax-based result.
}
}
export {
getBaseTypes,
getTypeSymbol,
isAliasSymbol,
isConstEnumSymbol,
isDefaultLibrarySymbol,
isNullishType,
isStringMappingType,
isTemplateLiteralType,
isTypeParameterType,
isUniqueSymbolType,
isUnknownType,
withTypeInformation,
};