eslint-plugin-type-graphql
Version:
Linter for TypeGraphQL decorators
192 lines • 8.36 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.getDecoratedProps = void 0;
const experimental_utils_1 = require("@typescript-eslint/experimental-utils");
const typescript_1 = require("typescript");
function getPossibleUnionName(typedNode) {
var _a, _b, _c, _d;
let typeAnnotation;
if (typedNode.type === experimental_utils_1.AST_NODE_TYPES.Identifier || typedNode.type === experimental_utils_1.AST_NODE_TYPES.PropertyDefinition) {
typeAnnotation = (_a = typedNode.typeAnnotation) === null || _a === void 0 ? void 0 : _a.typeAnnotation;
}
else if (typedNode.type === experimental_utils_1.AST_NODE_TYPES.MethodDefinition && typedNode.kind === 'method') {
typeAnnotation = (_b = typedNode.value.returnType) === null || _b === void 0 ? void 0 : _b.typeAnnotation;
}
if (!typeAnnotation) {
return;
}
if (typeAnnotation.type === experimental_utils_1.AST_NODE_TYPES.TSTypeReference &&
['Array', 'Promise'].includes(typeAnnotation.typeName.name)) {
typeAnnotation = ((_d = (_c = typeAnnotation.typeParameters) === null || _c === void 0 ? void 0 : _c.params) === null || _d === void 0 ? void 0 : _d[0]) || typeAnnotation;
}
if (typeAnnotation.type === experimental_utils_1.AST_NODE_TYPES.TSTypeQuery &&
typeAnnotation.exprName.type === experimental_utils_1.AST_NODE_TYPES.Identifier) {
return typeAnnotation.exprName.name;
}
return undefined;
}
function getDecoratedProps({ decoratorNode, checker, parserServices }) {
var _a, _b;
const parent = decoratorNode.parent;
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(parent);
let type = checker.getTypeAtLocation(tsNode);
let typeNode = undefined;
if (parent.type === experimental_utils_1.AST_NODE_TYPES.MethodDefinition) {
typeNode = (_a = parent.value.returnType) === null || _a === void 0 ? void 0 : _a.typeAnnotation;
if (parent.kind === 'method' && type.getCallSignatures()[0]) {
type = type.getCallSignatures()[0].getReturnType();
}
}
else {
typeNode = (_b = parent.typeAnnotation) === null || _b === void 0 ? void 0 : _b.typeAnnotation;
}
return {
kind: parent.type,
type: getDecoratedType(type, getPossibleUnionName(parent)),
node: parent,
typeNode,
};
}
exports.getDecoratedProps = getDecoratedProps;
function getDecoratedType(type, possibleUnionName) {
var _a, _b;
// Check whether TypeScript was able to determine the type
if (type.flags === typescript_1.TypeFlags.Any) {
return null;
}
// Check wheter the type is a promise
if (type.flags === typescript_1.TypeFlags.Object && ((_a = type.symbol) === null || _a === void 0 ? void 0 : _a.escapedName) === 'Promise') {
const typeArguments = type.resolvedTypeArguments;
const innerType = getDecoratedType(typeArguments[0], possibleUnionName);
if (!(innerType === null || innerType === void 0 ? void 0 : innerType.isValid)) {
return innerType;
}
return Object.assign(Object.assign({}, innerType), { isPromise: true });
}
// Check whether the type is nullable or undefinable
let isNullable = false;
let isUndefinable = false;
let isBooleanUnion = false;
if (type.flags === typescript_1.TypeFlags.Union) {
const innerTypes = [...type.types];
for (let i = innerTypes.length - 1; i >= 0; i--) {
if (innerTypes[i].flags === typescript_1.TypeFlags.Null) {
isNullable = true;
innerTypes.splice(i, 1);
}
else if (innerTypes[i].flags === typescript_1.TypeFlags.Undefined) {
isUndefinable = true;
innerTypes.splice(i, 1);
}
}
if (innerTypes.length === 0) {
// null/undefined-only types are not supported;
return {
isValid: false,
nullOrUndefinedType: true,
};
}
else if (innerTypes.length > 1) {
// Check whether this is a boolean
if (innerTypes.length === 2 && innerTypes.every((innerType) => innerType.flags === typescript_1.TypeFlags.BooleanLiteral)) {
isBooleanUnion = true;
}
else {
// Check whether all types in union are part of the same enumeration (type is actually a nullable/undefinable enumartion)
const enumerationNames = innerTypes.map(
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
(innerType) => innerType.flags & typescript_1.TypeFlags.EnumLiteral && innerType.symbol.parent.name);
const isSameEnumeration = !!enumerationNames[0] && enumerationNames.every((enumerationName) => enumerationName === enumerationNames[0]);
if (!isSameEnumeration) {
// Not an enumation. Union types may still be valid, for example when created using createUnionType. If we found a possible union type in the AST, we will use it
if (possibleUnionName) {
return {
isValid: true,
name: possibleUnionName,
isNullable,
isUndefinable,
isArray: false,
};
}
else {
return {
isValid: false,
unionType: true,
};
}
}
}
}
type = innerTypes[0];
}
// Check whether the type is an array
if (type.flags === typescript_1.TypeFlags.Object && ((_b = type.symbol) === null || _b === void 0 ? void 0 : _b.name) === 'Array') {
const typeArguments = type.resolvedTypeArguments;
const innerType = getDecoratedType(typeArguments[0], possibleUnionName);
if (!innerType) {
return null;
}
if (!innerType.isValid) {
// Inner type is invalid
return innerType;
}
else if (innerType.isPromise || innerType.isArray) {
// Types are nested in an unsupported way
return {
isValid: false,
tooComplex: true,
};
}
return Object.assign(Object.assign({}, innerType), { isArray: true, isArrayNullable: isNullable, isArrayUndefinable: isUndefinable });
}
// Check whether the type is a literal
if (type.flags === typescript_1.TypeFlags.Number) {
return {
isValid: true,
name: 'number',
isNullable,
isUndefinable,
isArray: false,
};
}
else if (type.flags === typescript_1.TypeFlags.String) {
return {
isValid: true,
name: 'string',
isNullable,
isUndefinable,
isArray: false,
};
}
else if (type.flags & typescript_1.TypeFlags.Boolean || isBooleanUnion) {
return {
isValid: true,
name: 'boolean',
isNullable,
isUndefinable,
isArray: false,
};
}
// Check whether the type is an object or enum
if (type.flags & typescript_1.TypeFlags.EnumLiteral || type.flags === typescript_1.TypeFlags.TypeParameter || type.flags === typescript_1.TypeFlags.Object) {
let symbol = type.symbol;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
if ((symbol === null || symbol === void 0 ? void 0 : symbol.flags) === typescript_1.SymbolFlags.EnumMember && symbol.parent.flags === typescript_1.SymbolFlags.RegularEnum) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
symbol = symbol.parent;
}
return {
isValid: true,
name: symbol === null || symbol === void 0 ? void 0 : symbol.name,
isNullable,
isUndefinable,
isArray: false,
};
}
// Other types are unsupported
return {
isValid: false,
unknownType: true,
};
}
//# sourceMappingURL=decoratedValue.js.map