Skip to content

Commit 8077d48

Browse files
committed
fix(no-unnecessary-type-arguments): handle Partial aliases in default-type checks (#862)
fixes #861
1 parent 50221c3 commit 8077d48

File tree

3 files changed

+118
-27
lines changed

3 files changed

+118
-27
lines changed

internal/rule_tester/__snapshots__/no-unnecessary-type-arguments.snap

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,3 +390,30 @@ Message: This is the default value for this type parameter, so it can be omitted
390390
| ~~~
391391
3 |
392392
---
393+
394+
[TestNoUnnecessaryTypeArguments/invalid-43 - 1]
395+
Diagnostic 1: isDefaultParameterValue (10:3 - 10:5)
396+
Message: This is the default value for this type parameter, so it can be omitted.
397+
9 | function f<T = Foo>() {}
398+
10 | f<Bar>();
399+
| ~~~
400+
11 |
401+
---
402+
403+
[TestNoUnnecessaryTypeArguments/invalid-44 - 1]
404+
Diagnostic 1: canBeInferred (3:52 - 3:62)
405+
Message: This value can be trivially inferred for this type parameter, so it can be omitted.
406+
2 | declare function useState<T>(initialState: T | (() => T)): [T, (value: T) => void];
407+
3 | const [bookmarkedIds, setBookmarkedIds] = useState<Set<string>>(new Set());
408+
| ~~~~~~~~~~~
409+
4 |
410+
---
411+
412+
[TestNoUnnecessaryTypeArguments/invalid-45 - 1]
413+
Diagnostic 1: canBeInferred (3:33 - 3:43)
414+
Message: This value can be trivially inferred for this type parameter, so it can be omitted.
415+
2 | declare function useRef<T>(initialValue: T): { current: T };
416+
3 | const activeIndexesRef = useRef<Set<number>>(new Set());
417+
| ~~~~~~~~~~~
418+
4 |
419+
---

internal/rules/no_unnecessary_type_arguments/no_unnecessary_type_arguments.go

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -55,18 +55,6 @@ func areTypesEquivalent(typeChecker *checker.Checker, a *checker.Type, b *checke
5555
return checker.Checker_isTypeAssignableTo(typeChecker, a, b) && checker.Checker_isTypeAssignableTo(typeChecker, b, a)
5656
}
5757

58-
func areDefaultValueTypesEquivalent(typeChecker *checker.Checker, a *checker.Type, b *checker.Type) bool {
59-
if a == b {
60-
return true
61-
}
62-
63-
if utils.IsTypeAnyType(a) || utils.IsTypeAnyType(b) || a.Flags() != b.Flags() {
64-
return false
65-
}
66-
67-
return checker.Checker_isTypeIdenticalTo(typeChecker, a, b)
68-
}
69-
7058
var NoUnnecessaryTypeArgumentsRule = rule.Rule{
7159
Name: "no-unnecessary-type-arguments",
7260
Run: func(ctx rule.RuleContext, options any) rule.RuleListeners {
@@ -159,9 +147,6 @@ var NoUnnecessaryTypeArgumentsRule = rule.Rule{
159147
typeParameter := parameters[lastParamIndex]
160148

161149
typeArgumentType := ctx.TypeChecker.GetTypeAtLocation(typeArgument)
162-
if utils.IsIntrinsicErrorType(typeArgumentType) {
163-
return
164-
}
165150

166151
if callOrNewExpr != nil {
167152
signature := checker.Checker_getResolvedSignature(ctx.TypeChecker, callOrNewExpr, nil, checker.CheckModeNormal)
@@ -212,7 +197,7 @@ var NoUnnecessaryTypeArgumentsRule = rule.Rule{
212197
}
213198

214199
defaultTypeValue := ctx.TypeChecker.GetTypeAtLocation(defaultType)
215-
if utils.IsIntrinsicErrorType(defaultTypeValue) || !areDefaultValueTypesEquivalent(ctx.TypeChecker, defaultTypeValue, typeArgumentType) {
200+
if !areTypesEquivalent(ctx.TypeChecker, defaultTypeValue, typeArgumentType) {
216201
return
217202
}
218203

internal/rules/no_unnecessary_type_arguments/no_unnecessary_type_arguments_test.go

Lines changed: 90 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -311,17 +311,6 @@ f<any>();
311311
{Code: `
312312
function f<T = any>() {}
313313
f<string>();
314-
`},
315-
{Code: `
316-
interface Foo {
317-
foo?: string
318-
}
319-
interface Bar extends Foo {
320-
bar?: string
321-
}
322-
323-
function f<T = Foo>() {}
324-
f<Bar>();
325314
`},
326315
// https://github.com/oxc-project/oxc/issues/13164
327316
{Code: `
@@ -334,6 +323,31 @@ interface TestInterface {
334323
type OneParam<T = any, U = any, V = any> = T;
335324
interface TestInterface {
336325
prop?: OneParam<string, number>; // TypeScript error, but shouldn't panic
326+
}
327+
`},
328+
// https://github.com/oxc-project/tsgolint/issues/861
329+
{Code: `
330+
type Data = Record<never, never>
331+
332+
type LocaleData<T extends Data = Data> = Record<string, T>
333+
334+
interface Data1 { a: string }
335+
336+
type Data2 = Partial<Data1>
337+
338+
type LocaleData2 = LocaleData<Data2>
339+
`},
340+
{Code: `
341+
type Data = Record<never, never>
342+
343+
type LocaleData<T extends Data = Data> = Record<string, T>
344+
345+
interface Data1 { a: string }
346+
347+
type Data2 = Partial<Data1>
348+
349+
interface Wrapper {
350+
value: LocaleData<Data2>
337351
}
338352
`},
339353
}, []rule_tester.InvalidTestCase{
@@ -1151,5 +1165,70 @@ declare type MessageEventHandler = ((ev: MessageEvent) => any) | null;
11511165
},
11521166
},
11531167
},
1168+
{
1169+
Code: `
1170+
interface Foo {
1171+
foo?: string
1172+
}
1173+
interface Bar extends Foo {
1174+
bar?: string
1175+
}
1176+
1177+
function f<T = Foo>() {}
1178+
f<Bar>();
1179+
`,
1180+
Output: []string{`
1181+
interface Foo {
1182+
foo?: string
1183+
}
1184+
interface Bar extends Foo {
1185+
bar?: string
1186+
}
1187+
1188+
function f<T = Foo>() {}
1189+
f();
1190+
`,
1191+
},
1192+
Errors: []rule_tester.InvalidTestCaseError{
1193+
{
1194+
Line: 10,
1195+
MessageId: "isDefaultParameterValue",
1196+
},
1197+
},
1198+
},
1199+
{
1200+
Code: `
1201+
declare function useState<T>(initialState: T | (() => T)): [T, (value: T) => void];
1202+
const [bookmarkedIds, setBookmarkedIds] = useState<Set<string>>(new Set());
1203+
`,
1204+
Output: []string{`
1205+
declare function useState<T>(initialState: T | (() => T)): [T, (value: T) => void];
1206+
const [bookmarkedIds, setBookmarkedIds] = useState(new Set());
1207+
`,
1208+
},
1209+
Errors: []rule_tester.InvalidTestCaseError{
1210+
{
1211+
Line: 3,
1212+
MessageId: "canBeInferred",
1213+
},
1214+
},
1215+
},
1216+
{
1217+
Code: `
1218+
declare function useRef<T>(initialValue: T): { current: T };
1219+
const activeIndexesRef = useRef<Set<number>>(new Set());
1220+
`,
1221+
Output: []string{`
1222+
declare function useRef<T>(initialValue: T): { current: T };
1223+
const activeIndexesRef = useRef(new Set());
1224+
`,
1225+
},
1226+
Errors: []rule_tester.InvalidTestCaseError{
1227+
{
1228+
Line: 3,
1229+
MessageId: "canBeInferred",
1230+
},
1231+
},
1232+
},
11541233
})
11551234
}

0 commit comments

Comments
 (0)