1 | "use strict";
|
2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
3 | if (k2 === undefined) k2 = k;
|
4 | Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
5 | }) : (function(o, m, k, k2) {
|
6 | if (k2 === undefined) k2 = k;
|
7 | o[k2] = m[k];
|
8 | }));
|
9 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
10 | Object.defineProperty(o, "default", { enumerable: true, value: v });
|
11 | }) : function(o, v) {
|
12 | o["default"] = v;
|
13 | });
|
14 | var __importStar = (this && this.__importStar) || function (mod) {
|
15 | if (mod && mod.__esModule) return mod;
|
16 | var result = {};
|
17 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
18 | __setModuleDefault(result, mod);
|
19 | return result;
|
20 | };
|
21 | Object.defineProperty(exports, "__esModule", { value: true });
|
22 | const utils_1 = require("@typescript-eslint/utils");
|
23 | const tsutils = __importStar(require("tsutils"));
|
24 | const ts = __importStar(require("typescript"));
|
25 | const util = __importStar(require("../util"));
|
26 | exports.default = util.createRule({
|
27 | name: 'non-nullable-type-assertion-style',
|
28 | meta: {
|
29 | docs: {
|
30 | description: 'Prefers a non-null assertion over explicit type cast when possible',
|
31 | recommended: false,
|
32 | requiresTypeChecking: true,
|
33 | suggestion: true,
|
34 | },
|
35 | fixable: 'code',
|
36 | messages: {
|
37 | preferNonNullAssertion: 'Use a ! assertion to more succinctly remove null and undefined from the type.',
|
38 | },
|
39 | schema: [],
|
40 | type: 'suggestion',
|
41 | },
|
42 | defaultOptions: [],
|
43 | create(context) {
|
44 | const parserServices = util.getParserServices(context);
|
45 | const checker = parserServices.program.getTypeChecker();
|
46 | const sourceCode = context.getSourceCode();
|
47 | const getTypesIfNotLoose = (node) => {
|
48 | const type = checker.getTypeAtLocation(parserServices.esTreeNodeToTSNodeMap.get(node));
|
49 | if (tsutils.isTypeFlagSet(type, ts.TypeFlags.Any | ts.TypeFlags.Unknown)) {
|
50 | return undefined;
|
51 | }
|
52 | return tsutils.unionTypeParts(type);
|
53 | };
|
54 | const couldBeNullish = (type) => {
|
55 | if (type.flags & ts.TypeFlags.TypeParameter) {
|
56 | const constraint = type.getConstraint();
|
57 | return constraint == null || couldBeNullish(constraint);
|
58 | }
|
59 | else if (tsutils.isUnionType(type)) {
|
60 | for (const part of type.types) {
|
61 | if (couldBeNullish(part)) {
|
62 | return true;
|
63 | }
|
64 | }
|
65 | return false;
|
66 | }
|
67 | else {
|
68 | return ((type.flags & (ts.TypeFlags.Null | ts.TypeFlags.Undefined)) !== 0);
|
69 | }
|
70 | };
|
71 | const sameTypeWithoutNullish = (assertedTypes, originalTypes) => {
|
72 | const nonNullishOriginalTypes = originalTypes.filter(type => (type.flags & (ts.TypeFlags.Null | ts.TypeFlags.Undefined)) === 0);
|
73 | if (nonNullishOriginalTypes.length === originalTypes.length) {
|
74 | return false;
|
75 | }
|
76 | for (const assertedType of assertedTypes) {
|
77 | if (couldBeNullish(assertedType) ||
|
78 | !nonNullishOriginalTypes.includes(assertedType)) {
|
79 | return false;
|
80 | }
|
81 | }
|
82 | for (const originalType of nonNullishOriginalTypes) {
|
83 | if (!assertedTypes.includes(originalType)) {
|
84 | return false;
|
85 | }
|
86 | }
|
87 | return true;
|
88 | };
|
89 | const isConstAssertion = (node) => {
|
90 | return (node.typeAnnotation.type === utils_1.AST_NODE_TYPES.TSTypeReference &&
|
91 | node.typeAnnotation.typeName.type === utils_1.AST_NODE_TYPES.Identifier &&
|
92 | node.typeAnnotation.typeName.name === 'const');
|
93 | };
|
94 | return {
|
95 | 'TSAsExpression, TSTypeAssertion'(node) {
|
96 | if (isConstAssertion(node)) {
|
97 | return;
|
98 | }
|
99 | const originalTypes = getTypesIfNotLoose(node.expression);
|
100 | if (!originalTypes) {
|
101 | return;
|
102 | }
|
103 | const assertedTypes = getTypesIfNotLoose(node.typeAnnotation);
|
104 | if (!assertedTypes) {
|
105 | return;
|
106 | }
|
107 | if (sameTypeWithoutNullish(assertedTypes, originalTypes)) {
|
108 | context.report({
|
109 | fix(fixer) {
|
110 | return fixer.replaceText(node, `${sourceCode.getText(node.expression)}!`);
|
111 | },
|
112 | messageId: 'preferNonNullAssertion',
|
113 | node,
|
114 | });
|
115 | }
|
116 | },
|
117 | };
|
118 | },
|
119 | });
|
120 |
|
\ | No newline at end of file |