diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index d2f12978704e..aa3b0e958132 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -65,7 +65,7 @@ "natural-compare-lite": "^1.4.0", "natural-compare": "^1.4.0", "semver": "^7.5.0", - "ts-api-utils": "^1.0.0" + "ts-api-utils": "^1.0.1" }, "devDependencies": { "@types/debug": "*", diff --git a/packages/type-utils/package.json b/packages/type-utils/package.json index 5f3a3e79ac09..9223a854022e 100644 --- a/packages/type-utils/package.json +++ b/packages/type-utils/package.json @@ -48,7 +48,7 @@ "@typescript-eslint/typescript-estree": "5.61.0", "@typescript-eslint/utils": "5.61.0", "debug": "^4.3.4", - "ts-api-utils": "^1.0.0" + "ts-api-utils": "^1.0.1" }, "devDependencies": { "@typescript-eslint/parser": "5.61.0", diff --git a/packages/type-utils/src/TypeOrValueSpecifier.ts b/packages/type-utils/src/TypeOrValueSpecifier.ts index 5542bf7d6022..12c7679349d9 100644 --- a/packages/type-utils/src/TypeOrValueSpecifier.ts +++ b/packages/type-utils/src/TypeOrValueSpecifier.ts @@ -1,6 +1,7 @@ import { getCanonicalFileName } from '@typescript-eslint/typescript-estree'; import type { JSONSchema4 } from '@typescript-eslint/utils/json-schema'; import path from 'path'; +import * as tsutils from 'ts-api-utils'; import type * as ts from 'typescript'; interface FileSpecifier { @@ -122,6 +123,9 @@ function specifierNameMatches(type: ts.Type, name: string[] | string): boolean { if (typeof name === 'string') { name = [name]; } + if (name.some(item => item === type.intrinsicName)) { + return true; + } const symbol = type.aliasSymbol ?? type.getSymbol(); if (symbol === undefined) { return false; @@ -163,11 +167,29 @@ function typeDeclaredInPackage( ); } +function typeDeclaredInLib( + declarationFiles: ts.SourceFile[], + program: ts.Program, +): boolean { + // Assertion: The type is not an error type. + + // Intrinsic type (i.e. string, number, boolean, etc) - Treat it as if it's from lib. + if (declarationFiles.length === 0) { + return true; + } + return declarationFiles.some(declaration => + program.isSourceFileDefaultLibrary(declaration), + ); +} + export function typeMatchesSpecifier( type: ts.Type, specifier: TypeOrValueSpecifier, program: ts.Program, ): boolean { + if (tsutils.isIntrinsicErrorType(type)) { + return false; + } if (typeof specifier === 'string') { return specifierNameMatches(type, specifier); } @@ -183,9 +205,7 @@ export function typeMatchesSpecifier( case 'file': return typeDeclaredInFile(specifier.path, declarationFiles, program); case 'lib': - return declarationFiles.some(declaration => - program.isSourceFileDefaultLibrary(declaration), - ); + return typeDeclaredInLib(declarationFiles, program); case 'package': return typeDeclaredInPackage(specifier.package, declarationFiles); } diff --git a/packages/type-utils/tests/TypeOrValueSpecifier.test.ts b/packages/type-utils/tests/TypeOrValueSpecifier.test.ts index 2b2f68926f15..8ddca54d3b34 100644 --- a/packages/type-utils/tests/TypeOrValueSpecifier.test.ts +++ b/packages/type-utils/tests/TypeOrValueSpecifier.test.ts @@ -269,6 +269,19 @@ describe('TypeOrValueSpecifier', () => { ['type Test = RegExp;', { from: 'lib', name: ['BigInt', 'Date'] }], ])("doesn't match a mismatched lib specifier: %s", runTestNegative); + it.each<[string, TypeOrValueSpecifier]>([ + ['type Test = string;', { from: 'lib', name: 'string' }], + ['type Test = string;', { from: 'lib', name: ['string', 'number'] }], + ])('matches a matching intrinsic type specifier: %s', runTestPositive); + + it.each<[string, TypeOrValueSpecifier]>([ + ['type Test = string;', { from: 'lib', name: 'number' }], + ['type Test = string;', { from: 'lib', name: ['number', 'boolean'] }], + ])( + "doesn't match a mismatched intrinsic type specifier: %s", + runTestNegative, + ); + it.each<[string, TypeOrValueSpecifier]>([ [ 'import type {Node} from "typescript"; type Test = Node;', @@ -405,5 +418,10 @@ describe('TypeOrValueSpecifier', () => { { from: 'package', name: ['RegExp', 'BigInt'], package: 'foo-package' }, ], ])("doesn't match a mismatched specifier type: %s", runTestNegative); + + it.each<[string, TypeOrValueSpecifier]>([ + ['type Test = Foo;', { from: 'lib', name: 'Foo' }], + ['type Test = Foo;', { from: 'lib', name: ['Foo', 'number'] }], + ])("doesn't match an error type: %s", runTestNegative); }); }); diff --git a/packages/typescript-estree/package.json b/packages/typescript-estree/package.json index b910c6d8b3bc..1905ece14fcc 100644 --- a/packages/typescript-estree/package.json +++ b/packages/typescript-estree/package.json @@ -58,7 +58,7 @@ "globby": "^11.1.0", "is-glob": "^4.0.3", "semver": "^7.5.0", - "ts-api-utils": "^1.0.0" + "ts-api-utils": "^1.0.1" }, "devDependencies": { "@babel/code-frame": "*", diff --git a/yarn.lock b/yarn.lock index 8e2d003073ab..cbbf418e3f60 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14979,10 +14979,10 @@ trough@^1.0.0: resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406" integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA== -ts-api-utils@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.0.0.tgz#bec2b0f3af409e5acd547dbf1d14e8261459bc42" - integrity sha512-ycbj7cbgdeLc5i7xhxewYjWOoMzeVz4PiKvkWC/fVjfbt4ToHCvotIzD+GB1iYn1R+kaQG0JdET1ZNZwl4nXUQ== +ts-api-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.0.1.tgz#8144e811d44c749cd65b2da305a032510774452d" + integrity sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A== ts-essentials@^2.0.3: version "2.0.12"
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: