Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

fix(eslint-plugin): add getConstraintInfo to handle generic constraints better#10496

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
Show all changes
17 commits
Select commitHold shift + click to select a range
d7bcdc6
add getConstraintTypeInfoAtLocation
typescript-eslint[bot]Dec 13, 2024
cd97692
revisit #10314
kirkwaiblingerDec 13, 2024
3626dcb
handle #10443
kirkwaiblingerDec 13, 2024
bc12e04
tweak tests
kirkwaiblingerDec 14, 2024
f0ebffa
Apply suggestions from code review
kirkwaiblingerDec 14, 2024
2fa6426
code review
kirkwaiblingerDec 14, 2024
2e0bd42
Merge branch 'main' into get-constraint-type-info-at-location
kirkwaiblingerDec 14, 2024
e753c02
deal with deprecated API lint failures
kirkwaiblingerDec 14, 2024
11d3a0a
space
kirkwaiblingerDec 14, 2024
5d7cab6
remove vfs, derp
kirkwaiblingerDec 16, 2024
f383280
typo
kirkwaiblingerDec 16, 2024
c8c985c
Merge branch 'main' into get-constraint-type-info-at-location
kirkwaiblingerDec 27, 2024
2aecb98
simplify
kirkwaiblingerDec 28, 2024
2a5c248
more simplify
kirkwaiblingerDec 28, 2024
c646ffb
jsdoc
kirkwaiblingerDec 28, 2024
943dc75
more better
kirkwaiblingerDec 29, 2024
4c9da08
better
kirkwaiblingerDec 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletionspackages/eslint-plugin/src/rules/await-thenable.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -51,10 +51,17 @@ export default createRule<[], MessageId>({

return {
AwaitExpression(node): void {
const type = services.getTypeAtLocation(node.argument);

const originalNode = services.esTreeNodeToTSNodeMap.get(node);
const certainty = needsToBeAwaited(checker, originalNode, type);
const awaitArgumentEsNode = node.argument;
const awaitArgumentType =
services.getTypeAtLocation(awaitArgumentEsNode);
const awaitArgumentTsNode =
services.esTreeNodeToTSNodeMap.get(awaitArgumentEsNode);

const certainty = needsToBeAwaited(
checker,
awaitArgumentTsNode,
awaitArgumentType,
);

if (certainty === Awaitable.Never) {
context.report({
Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,7 +6,7 @@ import * as ts from 'typescript';

import {
createRule,
getConstrainedTypeAtLocation,
getConstraintInfo,
getParserServices,
isStrongPrecedenceNode,
} from '../util';
Expand DownExpand Up@@ -85,6 +85,7 @@ export default createRule<Options, MessageIds>({
],
create(context, [options]) {
const services = getParserServices(context);
const checker = services.program.getTypeChecker();

function getBooleanComparison(
node: TSESTree.BinaryExpression,
Expand All@@ -94,19 +95,23 @@ export default createRule<Options, MessageIds>({
return undefined;
}

constexpressionType = getConstrainedTypeAtLocation(
services,
comparison.expression,
const{ constraintType, isTypeParameter } = getConstraintInfo(
checker,
services.getTypeAtLocation(comparison.expression),
);

if (isBooleanType(expressionType)) {
if (isTypeParameter && constraintType == null) {
return undefined;
}

if (isBooleanType(constraintType)) {
return {
...comparison,
expressionIsNullableBoolean: false,
};
}

if (isNullableBoolean(expressionType)) {
if (isNullableBoolean(constraintType)) {
return {
...comparison,
expressionIsNullableBoolean: true,
Expand Down
23 changes: 10 additions & 13 deletionspackages/eslint-plugin/src/rules/no-unnecessary-condition.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@ import * as ts from 'typescript';
import {
createRule,
getConstrainedTypeAtLocation,
getConstraintInfo,
getParserServices,
getTypeName,
getTypeOfPropertyOfName,
Expand DownExpand Up@@ -653,16 +654,7 @@ export default createRule<Options, MessageId>({
.getCallSignaturesOfType(
getConstrainedTypeAtLocation(services, callback),
)
.map(sig => sig.getReturnType())
.map(t => {
// TODO: use `getConstraintTypeInfoAtLocation` once it's merged
// https://github.com/typescript-eslint/typescript-eslint/pull/10496
if (tsutils.isTypeParameter(t)) {
return checker.getBaseConstraintOfType(t);
}

return t;
});
.map(sig => sig.getReturnType());

if (returnTypes.length === 0) {
// Not a callable function, e.g. `any`
Expand All@@ -673,16 +665,21 @@ export default createRule<Options, MessageId>({
let hasTruthyReturnTypes = false;

for (const type of returnTypes) {
const { constraintType } = getConstraintInfo(checker, type);
// Predicate is always necessary if it involves `any` or `unknown`
if (!type || isTypeAnyType(type) || isTypeUnknownType(type)) {
if (
!constraintType ||
isTypeAnyType(constraintType) ||
isTypeUnknownType(constraintType)
) {
return;
}

if (isPossiblyFalsy(type)) {
if (isPossiblyFalsy(constraintType)) {
hasFalsyReturnTypes = true;
}

if (isPossiblyTruthy(type)) {
if (isPossiblyTruthy(constraintType)) {
hasTruthyReturnTypes = true;
}

Expand Down
60 changes: 60 additions & 0 deletionspackages/eslint-plugin/src/util/getConstraintInfo.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
import type * as ts from 'typescript';

import * as tsutils from 'ts-api-utils';

export interface ConstraintTypeInfoUnconstrained {
constraintType: undefined;
isTypeParameter: true;
}

export interface ConstraintTypeInfoConstrained {
constraintType: ts.Type;
isTypeParameter: true;
}

export interface ConstraintTypeInfoNonGeneric {
constraintType: ts.Type;
isTypeParameter: false;
}

export type ConstraintTypeInfo =
| ConstraintTypeInfoConstrained
| ConstraintTypeInfoNonGeneric
| ConstraintTypeInfoUnconstrained;

/**
* Returns whether the type is a generic and what its constraint is.
*
* If the type is not a generic, `isTypeParameter` will be `false`, and
* `constraintType` will be the same as the input type.
*
* If the type is a generic, and it is constrained, `isTypeParameter` will be
* `true`, and `constraintType` will be the constraint type.
*
* If the type is a generic, but it is not constrained, `constraintType` will be
* `undefined` (rather than an `unknown` type), due to https://github.com/microsoft/TypeScript/issues/60475
*
* Successor to {@link getConstrainedTypeAtLocation} due to https://github.com/typescript-eslint/typescript-eslint/issues/10438
*
* This is considered internal since it is unstable for now and may have breaking changes at any time.
* Use at your own risk.
*
* @internal
*
*/
export function getConstraintInfo(
checker: ts.TypeChecker,
type: ts.Type,
): ConstraintTypeInfo {
if (tsutils.isTypeParameter(type)) {
const constraintType = checker.getBaseConstraintOfType(type);
return {
constraintType,
isTypeParameter: true,
};
}
return {
constraintType: type,
isTypeParameter: false,
};
}
2 changes: 2 additions & 0 deletionspackages/eslint-plugin/src/util/index.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -23,9 +23,11 @@ export * from './objectIterators';
export * from './needsToBeAwaited';
export * from './scopeUtils';
export * from './types';
export * from './getConstraintInfo';

// this is done for convenience - saves migrating all of the old rules
export * from '@typescript-eslint/type-utils';

const {
applyDefault,
deepMerge,
Expand Down
14 changes: 6 additions & 8 deletionspackages/eslint-plugin/src/util/needsToBeAwaited.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,6 +6,8 @@ import {
} from '@typescript-eslint/type-utils';
import * as tsutils from 'ts-api-utils';

import { getConstraintInfo } from './getConstraintInfo';

export enum Awaitable {
Always,
Never,
Expand All@@ -17,24 +19,20 @@ export function needsToBeAwaited(
node: ts.Node,
type: ts.Type,
): Awaitable {
// can't use `getConstrainedTypeAtLocation` directly since it's bugged for
// unconstrained generics.
const constrainedType = !tsutils.isTypeParameter(type)
? type
: checker.getBaseConstraintOfType(type);
const { constraintType, isTypeParameter } = getConstraintInfo(checker, type);

// unconstrained generic types should be treated as unknown
if (constrainedType == null) {
if (isTypeParameter && constraintType == null) {
return Awaitable.May;
}

// `any` and `unknown` types may need to be awaited
if (isTypeAnyType(constrainedType) || isTypeUnknownType(constrainedType)) {
if (isTypeAnyType(constraintType) || isTypeUnknownType(constraintType)) {
return Awaitable.May;
}

// 'thenable' values should always be be awaited
if (tsutils.isThenableType(checker, node,constrainedType)) {
if (tsutils.isThenableType(checker, node,constraintType)) {
return Awaitable.Always;
}

Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -109,6 +109,20 @@ ruleTester.run('no-unnecessary-boolean-literal-compare', rule, {
},
"'false' === true;",
"'true' === false;",
`
const unconstrained: <T>(someCondition: T) => void = someCondition => {
if (someCondition === true) {
}
};
`,
`
const extendsUnknown: <T extends unknown>(
someCondition: T,
) => void = someCondition => {
if (someCondition === true) {
}
};
`,
],

invalid: [
Expand Down
Loading
Loading

[8]ページ先頭

©2009-2025 Movatter.jp