Skip to content

Commit 39beb25

Browse files
committedAug 5, 2024··
fix: checking of types from ts's lib are now more strict (#862)
1 parent c2c589c commit 39beb25

File tree

4 files changed

+77
-41
lines changed

4 files changed

+77
-41
lines changed
 

‎src/rules/immutable-data.ts

+11-5
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,7 @@ function isInChainCallAndFollowsNew(
478478
// Check for: new Array()
479479
if (
480480
isNewExpression(node) &&
481-
isArrayConstructorType(getTypeOfNode(node.callee, context))
481+
isArrayConstructorType(context, getTypeOfNode(node.callee, context))
482482
) {
483483
return true;
484484
}
@@ -491,7 +491,10 @@ function isInChainCallAndFollowsNew(
491491
// Check for: Array.from(iterable)
492492
if (
493493
arrayConstructorFunctions.some(isExpected(node.callee.property.name)) &&
494-
isArrayConstructorType(getTypeOfNode(node.callee.object, context))
494+
isArrayConstructorType(
495+
context,
496+
getTypeOfNode(node.callee.object, context),
497+
)
495498
) {
496499
return true;
497500
}
@@ -508,7 +511,10 @@ function isInChainCallAndFollowsNew(
508511
objectConstructorNewObjectReturningMethods.some(
509512
isExpected(node.callee.property.name),
510513
) &&
511-
isObjectConstructorType(getTypeOfNode(node.callee.object, context))
514+
isObjectConstructorType(
515+
context,
516+
getTypeOfNode(node.callee.object, context),
517+
)
512518
) {
513519
return true;
514520
}
@@ -582,7 +588,7 @@ function checkCallExpression(
582588
arrayMutatorMethods.has(node.callee.property.name) &&
583589
(!ignoreImmediateMutation ||
584590
!isInChainCallAndFollowsNew(node.callee, context)) &&
585-
isArrayType(getTypeOfNode(node.callee.object, context))
591+
isArrayType(context, getTypeOfNode(node.callee.object, context))
586592
) {
587593
if (ignoreNonConstDeclarations === false) {
588594
return {
@@ -627,7 +633,7 @@ function checkCallExpression(
627633
ignoreIdentifierPattern,
628634
ignoreAccessorPattern,
629635
) &&
630-
isObjectConstructorType(getTypeOfNode(node.callee.object, context))
636+
isObjectConstructorType(context, getTypeOfNode(node.callee.object, context))
631637
) {
632638
if (ignoreNonConstDeclarations === false) {
633639
return {

‎src/rules/prefer-readonly-type.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -495,7 +495,7 @@ function checkImplicitType(
495495
isIdentifier(declarator.id) &&
496496
declarator.id.typeAnnotation === undefined &&
497497
declarator.init !== null &&
498-
isArrayType(getTypeOfNode(declarator.init, context)) &&
498+
isArrayType(context, getTypeOfNode(declarator.init, context)) &&
499499
!ignoreCollections
500500
? [
501501
{

‎src/utils/tree.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ export function isInPromiseHandlerFunction<
149149
}
150150

151151
const objectType = getTypeOfNode(functionNode.parent.callee.object, context);
152-
return isPromiseType(objectType);
152+
return isPromiseType(context, objectType);
153153
}
154154

155155
/**

‎src/utils/type-guards.ts

+64-34
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,18 @@
33
*/
44

55
import { AST_NODE_TYPES, type TSESTree } from "@typescript-eslint/utils";
6-
import type { Type, UnionType } from "typescript";
6+
import type { RuleContext } from "@typescript-eslint/utils/ts-eslint";
7+
import typeMatchesSpecifier, {
8+
type TypeDeclarationSpecifier,
9+
} from "ts-declaration-location";
10+
import type { Program, Type, UnionType } from "typescript";
711

812
import typescript from "#/conditional-imports/typescript";
913

14+
const libSpecifier = {
15+
from: "lib",
16+
} satisfies TypeDeclarationSpecifier;
17+
1018
/*
1119
* TS Types.
1220
*/
@@ -429,41 +437,63 @@ export function isUnionType(type: Type): type is UnionType {
429437
return typescript !== undefined && type.flags === typescript.TypeFlags.Union;
430438
}
431439

432-
export function isArrayType(type: Type | null): boolean {
433-
return (
434-
type !== null &&
435-
(((type.symbol as unknown) !== undefined && type.symbol.name === "Array") ||
436-
(isUnionType(type) && type.types.some(isArrayType)))
437-
);
438-
}
439-
440-
export function isArrayConstructorType(type: Type | null): boolean {
441-
return (
442-
type !== null &&
443-
(((type.symbol as unknown) !== undefined &&
444-
type.symbol.name === "ArrayConstructor") ||
445-
(isUnionType(type) && type.types.some(isArrayConstructorType)))
446-
);
447-
}
448-
449-
export function isObjectConstructorType(type: Type | null): boolean {
450-
return (
451-
type !== null &&
452-
(((type.symbol as unknown) !== undefined &&
453-
type.symbol.name === "ObjectConstructor") ||
454-
(isUnionType(type) && type.types.some(isObjectConstructorType)))
455-
);
456-
}
457-
458440
export function isFunctionLikeType(type: Type | null): boolean {
459441
return type !== null && type.getCallSignatures().length > 0;
460442
}
461443

462-
export function isPromiseType(type: Type | null): boolean {
463-
return (
464-
type !== null &&
465-
(((type.symbol as unknown) !== undefined &&
466-
type.symbol.name === "Promise") ||
467-
(isUnionType(type) && type.types.some(isPromiseType)))
468-
);
444+
export function isArrayType(
445+
context: RuleContext<string, ReadonlyArray<unknown>>,
446+
type: Type | null,
447+
): boolean {
448+
return typeMatches(context, "Array", type);
449+
}
450+
451+
export function isArrayConstructorType(
452+
context: RuleContext<string, ReadonlyArray<unknown>>,
453+
type: Type | null,
454+
): boolean {
455+
return typeMatches(context, "ArrayConstructor", type);
456+
}
457+
458+
export function isObjectConstructorType(
459+
context: RuleContext<string, ReadonlyArray<unknown>>,
460+
type: Type | null,
461+
): boolean {
462+
return typeMatches(context, "ObjectConstructor", type);
463+
}
464+
465+
export function isPromiseType(
466+
context: RuleContext<string, ReadonlyArray<unknown>>,
467+
type: Type | null,
468+
): boolean {
469+
return typeMatches(context, "Promise", type);
470+
}
471+
472+
function typeMatches(
473+
context: RuleContext<string, ReadonlyArray<unknown>>,
474+
typeName: string,
475+
type: Type | null,
476+
): boolean {
477+
if (type === null) {
478+
return false;
479+
}
480+
const program = context.sourceCode.parserServices?.program ?? undefined;
481+
if (program === undefined) {
482+
return false;
483+
}
484+
return typeMatchesHelper(program, typeName)(type);
485+
}
486+
487+
function typeMatchesHelper(
488+
program: Program,
489+
typeName: string,
490+
): (type: Type) => boolean {
491+
return function test(type: Type) {
492+
return (
493+
((type.symbol as unknown) !== undefined &&
494+
type.symbol.name === typeName &&
495+
typeMatchesSpecifier(program, libSpecifier, type)) ||
496+
(isUnionType(type) && type.types.some(test))
497+
);
498+
};
469499
}

0 commit comments

Comments
 (0)
Please sign in to comment.