@@ -7,10 +7,10 @@ import * as util from '../util';
7
7
8
8
type Options = [
9
9
{
10
- allowNullable ?: boolean ;
11
10
allowNumber ?: boolean ;
12
11
allowBoolean ?: boolean ;
13
12
allowAny ?: boolean ;
13
+ allowNullable ?: boolean ;
14
14
} ,
15
15
] ;
16
16
@@ -33,10 +33,10 @@ export default util.createRule<Options, MessageId>({
33
33
{
34
34
type : 'object' ,
35
35
properties : {
36
- allowAny : { type : 'boolean' } ,
36
+ allowNumber : { type : 'boolean' } ,
37
37
allowBoolean : { type : 'boolean' } ,
38
+ allowAny : { type : 'boolean' } ,
38
39
allowNullable : { type : 'boolean' } ,
39
- allowNumber : { type : 'boolean' } ,
40
40
} ,
41
41
} ,
42
42
] ,
@@ -46,31 +46,40 @@ export default util.createRule<Options, MessageId>({
46
46
const service = util . getParserServices ( context ) ;
47
47
const typeChecker = service . program . getTypeChecker ( ) ;
48
48
49
- type BaseType =
50
- | 'string'
51
- | 'number'
52
- | 'bigint'
53
- | 'boolean'
54
- | 'null'
55
- | 'undefined'
56
- | 'any'
57
- | 'other' ;
58
-
59
- const allowedTypes : BaseType [ ] = [
60
- 'string' ,
61
- ...( options . allowNumber ? ( [ 'number' , 'bigint' ] as const ) : [ ] ) ,
62
- ...( options . allowBoolean ? ( [ 'boolean' ] as const ) : [ ] ) ,
63
- ...( options . allowNullable ? ( [ 'null' , 'undefined' ] as const ) : [ ] ) ,
64
- ...( options . allowAny ? ( [ 'any' ] as const ) : [ ] ) ,
65
- ] ;
66
-
67
- function isAllowedType ( types : BaseType [ ] ) : boolean {
68
- for ( const type of types ) {
69
- if ( ! allowedTypes . includes ( type ) ) {
70
- return false ;
71
- }
49
+ function isUnderlyingTypePrimitive ( type : ts . Type ) : boolean {
50
+ if ( util . isTypeFlagSet ( type , ts . TypeFlags . StringLike ) ) {
51
+ return true ;
52
+ }
53
+
54
+ if (
55
+ options . allowNumber &&
56
+ util . isTypeFlagSet (
57
+ type ,
58
+ ts . TypeFlags . NumberLike | ts . TypeFlags . BigIntLike ,
59
+ )
60
+ ) {
61
+ return true ;
72
62
}
73
- return true ;
63
+
64
+ if (
65
+ options . allowBoolean &&
66
+ util . isTypeFlagSet ( type , ts . TypeFlags . BooleanLike )
67
+ ) {
68
+ return true ;
69
+ }
70
+
71
+ if ( options . allowAny && util . isTypeFlagSet ( type , ts . TypeFlags . Any ) ) {
72
+ return true ;
73
+ }
74
+
75
+ if (
76
+ options . allowNullable &&
77
+ util . isTypeFlagSet ( type , ts . TypeFlags . Null | ts . TypeFlags . Undefined )
78
+ ) {
79
+ return true ;
80
+ }
81
+
82
+ return false ;
74
83
}
75
84
76
85
return {
@@ -80,70 +89,47 @@ export default util.createRule<Options, MessageId>({
80
89
return ;
81
90
}
82
91
83
- for ( const expr of node . expressions ) {
84
- const type = getNodeType ( expr ) ;
85
- if ( ! isAllowedType ( type ) ) {
92
+ for ( const expression of node . expressions ) {
93
+ if (
94
+ ! isUnderlyingExpressionTypeConfirmingTo (
95
+ expression ,
96
+ isUnderlyingTypePrimitive ,
97
+ )
98
+ ) {
86
99
context . report ( {
87
- node : expr ,
100
+ node : expression ,
88
101
messageId : 'invalidType' ,
89
102
} ) ;
90
103
}
91
104
}
92
105
} ,
93
106
} ;
94
107
95
- /**
96
- * Helper function to get base type of node
97
- * @param node the node to be evaluated.
98
- */
99
- function getNodeType ( node : TSESTree . Expression ) : BaseType [ ] {
100
- const tsNode = service . esTreeNodeToTSNodeMap . get ( node ) ;
101
- const type = util . getConstrainedTypeAtLocation ( typeChecker , tsNode ) ;
108
+ function isUnderlyingExpressionTypeConfirmingTo (
109
+ expression : TSESTree . Expression ,
110
+ predicate : ( underlyingType : ts . Type ) => boolean ,
111
+ ) : boolean {
112
+ return rec ( getExpressionNodeType ( expression ) ) ;
102
113
103
- return getBaseType ( type ) ;
104
- }
105
-
106
- function getBaseType ( type : ts . Type ) : BaseType [ ] {
107
- if ( type . isStringLiteral ( ) ) {
108
- return [ 'string' ] ;
109
- }
110
- if ( type . isNumberLiteral ( ) ) {
111
- return [ 'number' ] ;
112
- }
113
- if ( type . flags & ts . TypeFlags . BigIntLiteral ) {
114
- return [ 'bigint' ] ;
115
- }
116
- if ( type . flags & ts . TypeFlags . BooleanLiteral ) {
117
- return [ 'boolean' ] ;
118
- }
119
- if ( type . flags & ts . TypeFlags . Null ) {
120
- return [ 'null' ] ;
121
- }
122
- if ( type . flags & ts . TypeFlags . Undefined ) {
123
- return [ 'undefined' ] ;
124
- }
125
- if ( type . flags & ts . TypeFlags . Any ) {
126
- return [ 'any' ] ;
127
- }
114
+ function rec ( type : ts . Type ) : boolean {
115
+ if ( type . isUnion ( ) ) {
116
+ return type . types . every ( rec ) ;
117
+ }
128
118
129
- if ( type . isUnion ( ) ) {
130
- return type . types
131
- . map ( getBaseType )
132
- . reduce ( ( all , array ) => [ ...all , ...array ] , [ ] ) ;
133
- }
119
+ if ( type . isIntersection ( ) ) {
120
+ return type . types . some ( rec ) ;
121
+ }
134
122
135
- const stringType = typeChecker . typeToString ( type ) ;
136
- if (
137
- stringType === 'string' ||
138
- stringType === 'number' ||
139
- stringType === 'bigint' ||
140
- stringType === 'boolean' ||
141
- stringType === 'any'
142
- ) {
143
- return [ stringType ] ;
123
+ return predicate ( type ) ;
144
124
}
125
+ }
145
126
146
- return [ 'other' ] ;
127
+ /**
128
+ * Helper function to extract the TS type of an TSESTree expression.
129
+ */
130
+ function getExpressionNodeType ( node : TSESTree . Expression ) : ts . Type {
131
+ const tsNode = service . esTreeNodeToTSNodeMap . get ( node ) ;
132
+ return util . getConstrainedTypeAtLocation ( typeChecker , tsNode ) ;
147
133
}
148
134
} ,
149
135
} ) ;
0 commit comments