Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 8e0f14c

Browse files
committed
feat: support array and function types
1 parent 1810a8f commit 8e0f14c

File tree

3 files changed

+115
-30
lines changed

3 files changed

+115
-30
lines changed

packages/eslint-plugin/src/rules/naming-convention.ts

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
TSESTree,
55
TSESLint,
66
} from '@typescript-eslint/experimental-utils';
7+
import ts from 'typescript';
78
import * as util from '../util';
89

910
type MessageIds =
@@ -1138,31 +1139,39 @@ function isCorrectType(
11381139
const { esTreeNodeToTSNodeMap, program } = util.getParserServices(context);
11391140
const checker = program.getTypeChecker();
11401141
const tsNode = esTreeNodeToTSNodeMap.get(node);
1141-
const type = checker.getTypeAtLocation(tsNode);
1142-
const typeString = checker.typeToString(
1143-
// this will resolve things like true => boolean, 'a' => string and 1 => number
1144-
checker.getWidenedType(checker.getBaseTypeOfLiteralType(type)),
1145-
);
1142+
const type = checker
1143+
.getTypeAtLocation(tsNode)
1144+
// remove null and undefined from the type, as we don't care about it here
1145+
.getNonNullableType();
11461146

11471147
for (const allowedType of config.types) {
11481148
switch (allowedType) {
11491149
case TypeModifiers.array:
1150-
// TODO
1150+
if (
1151+
isAllTypesMatch(
1152+
type,
1153+
t => checker.isArrayType(t) || checker.isTupleType(t),
1154+
)
1155+
) {
1156+
return true;
1157+
}
11511158
break;
11521159

11531160
case TypeModifiers.function:
1154-
// TODO
1161+
if (isAllTypesMatch(type, t => t.getCallSignatures().length > 0)) {
1162+
return true;
1163+
}
11551164
break;
11561165

11571166
case TypeModifiers.boolean:
11581167
case TypeModifiers.number:
11591168
case TypeModifiers.string: {
1169+
const typeString = checker.typeToString(
1170+
// this will resolve things like true => boolean, 'a' => string and 1 => number
1171+
checker.getWidenedType(checker.getBaseTypeOfLiteralType(type)),
1172+
);
11601173
const allowedTypeString = TypeModifiers[allowedType];
1161-
if (
1162-
typeString === `${allowedTypeString}` ||
1163-
typeString === `${allowedTypeString} | null` ||
1164-
typeString === `${allowedTypeString} | null | undefined`
1165-
) {
1174+
if (typeString === allowedTypeString) {
11661175
return true;
11671176
}
11681177
break;
@@ -1173,6 +1182,20 @@ function isCorrectType(
11731182
return false;
11741183
}
11751184

1185+
/**
1186+
* @returns `true` if the type (or all union types) in the given type return true for the callback
1187+
*/
1188+
function isAllTypesMatch(
1189+
type: ts.Type,
1190+
cb: (type: ts.Type) => boolean,
1191+
): boolean {
1192+
if (type.isUnion()) {
1193+
return type.types.every(t => cb(t));
1194+
}
1195+
1196+
return cb(type);
1197+
}
1198+
11761199
export {
11771200
MessageIds,
11781201
Options,

packages/eslint-plugin/tests/rules/naming-convention.test.ts

Lines changed: 59 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -652,24 +652,24 @@ ruleTester.run('naming-convention', rule, {
652652
...createInvalidTestCases(cases),
653653
{
654654
code: `
655-
declare const string_camelCase: string;
656-
declare const string_camelCase: string | null;
657-
declare const string_camelCase: string | null | undefined;
658-
declare const string_camelCase: 'a' | null | undefined;
659-
declare const string_camelCase: string | 'a' | null | undefined;
660-
661-
declare const number_camelCase: number;
662-
declare const number_camelCase: number | null;
663-
declare const number_camelCase: number | null | undefined;
664-
declare const number_camelCase: 1 | null | undefined;
665-
declare const number_camelCase: number | 2 | null | undefined;
666-
667-
declare const boolean_camelCase: boolean;
668-
declare const boolean_camelCase: boolean | null;
669-
declare const boolean_camelCase: boolean | null | undefined;
670-
declare const boolean_camelCase: true | null | undefined;
671-
declare const boolean_camelCase: false | null | undefined;
672-
declare const boolean_camelCase: true | false | null | undefined;
655+
declare const string_camelCase01: string;
656+
declare const string_camelCase02: string | null;
657+
declare const string_camelCase03: string | null | undefined;
658+
declare const string_camelCase04: 'a' | null | undefined;
659+
declare const string_camelCase05: string | 'a' | null | undefined;
660+
661+
declare const number_camelCase06: number;
662+
declare const number_camelCase07: number | null;
663+
declare const number_camelCase08: number | null | undefined;
664+
declare const number_camelCase09: 1 | null | undefined;
665+
declare const number_camelCase10: number | 2 | null | undefined;
666+
667+
declare const boolean_camelCase11: boolean;
668+
declare const boolean_camelCase12: boolean | null;
669+
declare const boolean_camelCase13: boolean | null | undefined;
670+
declare const boolean_camelCase14: true | null | undefined;
671+
declare const boolean_camelCase15: false | null | undefined;
672+
declare const boolean_camelCase16: true | false | null | undefined;
673673
`,
674674
options: [
675675
{
@@ -694,5 +694,46 @@ ruleTester.run('naming-convention', rule, {
694694
parserOptions,
695695
errors: Array(16).fill({ messageId: 'doesNotMatchFormat' }),
696696
},
697+
{
698+
code: `
699+
declare const function_camelCase1: (() => void);
700+
declare const function_camelCase2: (() => void) | null;
701+
declare const function_camelCase3: (() => void) | null | undefined;
702+
declare const function_camelCase4: (() => void) | (() => string) | null | undefined;
703+
`,
704+
options: [
705+
{
706+
selector: 'variable',
707+
types: ['function'],
708+
format: ['snake_case'],
709+
prefix: ['function_'],
710+
},
711+
],
712+
parserOptions,
713+
errors: Array(4).fill({ messageId: 'doesNotMatchFormat' }),
714+
},
715+
{
716+
code: `
717+
declare const array_camelCase1: Array<number>;
718+
declare const array_camelCase2: ReadonlyArray<number> | null;
719+
declare const array_camelCase3: number[] | null | undefined;
720+
declare const array_camelCase4: readonly number[] | null | undefined;
721+
declare const array_camelCase5: number[] | (number | string)[] | null | undefined;
722+
declare const array_camelCase6: [] | null | undefined;
723+
declare const array_camelCase7: [number] | null | undefined;
724+
725+
declare const array_camelCase8: readonly number[] | Array<string> | [boolean] | null | undefined;
726+
`,
727+
options: [
728+
{
729+
selector: 'variable',
730+
types: ['array'],
731+
format: ['snake_case'],
732+
prefix: ['array_'],
733+
},
734+
],
735+
parserOptions,
736+
errors: Array(8).fill({ messageId: 'doesNotMatchFormat' }),
737+
},
697738
],
698739
});
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { TypeChecker, Type } from 'typescript';
2+
3+
declare module 'typescript' {
4+
interface TypeChecker {
5+
// internal TS APIs
6+
7+
/**
8+
* @returns `true` if the given type is an array type:
9+
* - Array<foo>
10+
* - ReadonlyArray<foo>
11+
* - foo[]
12+
* - readonly foo[]
13+
*/
14+
isArrayType(type: Type): boolean;
15+
/**
16+
* @returns `true` if the given type is a tuple type:
17+
* - [foo]
18+
*/
19+
isTupleType(type: Type): boolean;
20+
}
21+
}

0 commit comments

Comments
 (0)