Skip to content

Commit 8eeab3d

Browse files
authoredJan 17, 2024
Add decorator-related diagnostic codes to expectError (#207)
1 parent bb28db1 commit 8eeab3d

File tree

11 files changed

+279
-6
lines changed

11 files changed

+279
-6
lines changed
 

‎source/lib/compiler.ts

+16-6
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,16 @@ const expectErrorDiagnosticCodesToIgnore = new Set<DiagnosticCode>([
4545
DiagnosticCode.StringLiteralTypeIsNotAssignableToUnionTypeWithSuggestion,
4646
DiagnosticCode.ObjectLiteralMayOnlySpecifyKnownProperties,
4747
DiagnosticCode.ObjectLiteralMayOnlySpecifyKnownProperties2,
48+
DiagnosticCode.UnableToResolveSignatureOfClassDecorator,
49+
DiagnosticCode.UnableToResolveSignatureOfParameterDecorator,
50+
DiagnosticCode.UnableToResolveSignatureOfPropertyDecorator,
51+
DiagnosticCode.UnableToResolveSignatureOfMethodDecorator,
52+
DiagnosticCode.DecoratorCanOnlyDecorateMethodImplementation,
53+
DiagnosticCode.DecoratorFunctionReturnTypeNotAssignableToType,
54+
DiagnosticCode.DecoratorFunctionReturnTypeExpectedToBeVoidOrAny,
55+
DiagnosticCode.RuntimeWillInvokeDecoratorWithXArgumentsButDecoratorExpectsY,
56+
DiagnosticCode.RuntimeWillInvokeDecoratorWithXArgumentsButDecoratorExpectsAtLeastY,
57+
DiagnosticCode.AcceptsTooFewArgumentsToBeUsedAsDecoratorHere,
4858
]);
4959

5060
type IgnoreDiagnosticResult = 'preserve' | 'ignore' | Location;
@@ -76,19 +86,19 @@ const ignoreDiagnostic = (
7686

7787
// Diagnostic is inside of `expectError` clause
7888
if (diagnosticFileName === location.fileName && start > location.start && start < location.end) {
89+
if (expectErrorDiagnosticCodesToIgnore.has(diagnostic.code)) {
90+
return location;
91+
}
92+
7993
// Ignore syntactical errors
8094
if (diagnostic.code < 2000) {
8195
expectedErrors.delete(location);
8296
return 'preserve';
8397
}
8498

8599
// Set diagnostic code on `ExpectedError` to log
86-
if (!expectErrorDiagnosticCodesToIgnore.has(diagnostic.code)) {
87-
error.code = diagnostic.code;
88-
return 'preserve';
89-
}
90-
91-
return location;
100+
error.code = diagnostic.code;
101+
return 'preserve';
92102
}
93103
}
94104

‎source/lib/interfaces.ts

+10
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,17 @@ export interface Context {
2323
}
2424

2525
export enum DiagnosticCode {
26+
UnableToResolveSignatureOfClassDecorator = 1238,
27+
UnableToResolveSignatureOfParameterDecorator = 1239,
28+
UnableToResolveSignatureOfPropertyDecorator = 1240,
29+
UnableToResolveSignatureOfMethodDecorator = 1241,
30+
DecoratorCanOnlyDecorateMethodImplementation = 1249,
31+
DecoratorFunctionReturnTypeNotAssignableToType = 1270,
32+
DecoratorFunctionReturnTypeExpectedToBeVoidOrAny = 1271,
33+
RuntimeWillInvokeDecoratorWithXArgumentsButDecoratorExpectsY = 1278,
34+
RuntimeWillInvokeDecoratorWithXArgumentsButDecoratorExpectsAtLeastY = 1279,
2635
AwaitExpressionOnlyAllowedWithinAsyncFunction = 1308,
36+
AcceptsTooFewArgumentsToBeUsedAsDecoratorHere = 1329,
2737
TopLevelAwaitOnlyAllowedWhenModuleESNextOrSystem = 1378,
2838
GenericTypeRequiresTypeArguments = 2314,
2939
GenericTypeRequiresBetweenXAndYTypeArugments = 2707,

‎source/test/expect-error.ts

+12
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,18 @@ test('expectError for values (exactOptionalPropertyTypes enabled)', async t => {
5252
verify(t, diagnostics, []);
5353
});
5454

55+
test('expectError for decorators', async t => {
56+
const diagnostics = await tsd({cwd: path.join(__dirname, 'fixtures/expect-error/decorators')});
57+
58+
verify(t, diagnostics, []);
59+
});
60+
61+
test('expectError for experimental decorators', async t => {
62+
const diagnostics = await tsd({cwd: path.join(__dirname, 'fixtures/expect-error/experimental-decorators')});
63+
64+
verify(t, diagnostics, []);
65+
});
66+
5567
test('expectError should report missing diagnostic codes', async t => {
5668
const diagnostics = await tsd({cwd: path.join(__dirname, 'fixtures/expect-error/missing-diagnostic-code')});
5769

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
export declare class Base {
2+
dummy(a: string, b: number): boolean;
3+
}
4+
5+
export function classDec<T extends new (a: number, b: boolean) => Base>(value: T, context: ClassDecoratorContext<T>): T;
6+
export function methodDec(value: (this: Base, a: string, b: number) => boolean, context: ClassMethodDecoratorContext<Base, (this:Base, a: string, b: number) => boolean>): (this: Base, a: string, b: number) => boolean
7+
export function getterDec(value: (this: Base) => number, context: ClassGetterDecoratorContext<Base, number>): (this: Base) => number;
8+
export function setterDec(value: (this: Base, value: number) => void, context: ClassSetterDecoratorContext<Base, number>): (this: Base, value: number) => void;
9+
export function accessorDec(value: ClassAccessorDecoratorTarget<Base, string>, context: ClassAccessorDecoratorContext<Base, string>): ClassAccessorDecoratorResult<Base, string>;
10+
export function fieldDec(value: undefined, context: ClassFieldDecoratorContext<Base, number>): (initialValue: number) => number;
11+
12+
export function tooFewArguments(value: Function): void;
13+
export function badReturnType(value: ClassAccessorDecoratorTarget<Base, number>, context: ClassAccessorDecoratorContext<Base, number>): number;
14+
15+
export function factory(arg: number): (value: (a: number) => void, context: ClassMethodDecoratorContext<unknown, (a: number) => void>) => void;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module.exports.classDec = (value, context) => {};
2+
module.exports.methodDec = (value, context) => {};
3+
module.exports.getterDec = (value, context) => {};
4+
module.exports.setterDec = (value, context) => {};
5+
module.exports.accessorDec = (value, context) => {};
6+
module.exports.fieldDec = (value, context) => {};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import {expectError} from '../../../..';
2+
import {Base, classDec, methodDec, getterDec, setterDec, accessorDec, fieldDec, tooFewArguments, badReturnType, factory} from '.';
3+
4+
expectError(@classDec class {}); // 1238, 1270
5+
expectError(() => { // 1238, 1270
6+
@classDec
7+
abstract class Test extends Base {}
8+
});
9+
10+
expectError(class extends Base { // 1241
11+
@methodDec static foo(a: string, b: number) { return true; }
12+
});
13+
expectError(class extends Base { // 1241, 1270
14+
@methodDec foo() {}
15+
});
16+
expectError(class { // 1241
17+
@methodDec foo(a: string, b: number) { return true; }
18+
});
19+
expectError(class extends Base { // 1249
20+
@methodDec override dummy(a: string, b: number): boolean
21+
dummy(): void
22+
dummy(a?: string, b?: number) : boolean|void {}
23+
});
24+
25+
expectError(class extends Base { // 1241
26+
@getterDec static get foo() { return 42; }
27+
});
28+
expectError(class extends Base { // 1241, 1270
29+
@getterDec get foo() { return "bar"; }
30+
});
31+
expectError(class { // 1241
32+
@getterDec get foo() { return 42; }
33+
});
34+
35+
expectError(class extends Base { // 1241
36+
@setterDec static set foo(value: number) {}
37+
});
38+
expectError(class extends Base { // 1241, 1270
39+
@setterDec set foo(value: string) {}
40+
});
41+
expectError(class { // 1241
42+
@setterDec set foo(value: number) {}
43+
});
44+
45+
expectError(class extends Base { // 1240, 1270
46+
@accessorDec static accessor foo = "bar";
47+
});
48+
expectError(class extends Base { // 1240, 1270
49+
@accessorDec accessor foo = 42;
50+
});
51+
expectError(class { // 1240, 1270
52+
@accessorDec accessor foo = "bar";
53+
});
54+
55+
expectError(class extends Base { // 1240
56+
@fieldDec static foo = 42;
57+
});
58+
expectError(class extends Base { // 1240, 1270
59+
@fieldDec foo = "bar"
60+
});
61+
expectError(class { // 1240
62+
@fieldDec foo = 42;
63+
});
64+
65+
expectError(class {
66+
@tooFewArguments foo() {}
67+
});
68+
expectError(class extends Base {
69+
@badReturnType accessor foo = 42;
70+
})
71+
72+
expectError(class {
73+
@factory("bar") foo(a: number) {}
74+
});
75+
expectError(class {
76+
@factory foo(a: number) {}
77+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"name": "foo"
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
export declare class Base {
2+
dummy(a: string, b: number): boolean;
3+
}
4+
5+
export function classDec<T extends new (a: number, b: boolean) => Base>(constructor: T): T;
6+
export function methodDec(target: Base, propertyKey: string, descriptor: TypedPropertyDescriptor<(a: number, b: number) => boolean>): void
7+
export function getterDec(target: Base, propertyKey: string, descriptor: TypedPropertyDescriptor<number>): void;
8+
export function setterDec(target: Base, propertyKey: string, descriptor: TypedPropertyDescriptor<number>): void;
9+
export function propertyDec(target: Base, propertyKey: string): void;
10+
export function parameterDec(target: Base, propertyKey: string, parameterIndex: number): void;
11+
12+
export function tooFewArguments(target: Base): PropertyDescriptor;
13+
export function badReturnType(target: Base, propertyKey: string, descriptor: PropertyDescriptor): number;
14+
15+
export function factory(arg: number): (target: Base, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module.exports.classDec = (constructor) => {};
2+
module.exports.methodDec = (target, propertyKey, descriptor) => {};
3+
module.exports.getterDec = (target, propertyKey, descriptor) => {};
4+
module.exports.setterDec = (target, propertyKey, descriptor) => {};
5+
module.exports.accessorDec = (target, propertyKey, descriptor) => {};
6+
module.exports.fieldDec = (target, propertyKey, descriptor) => {};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import {expectError} from '../../../..';
2+
import {Base, classDec, methodDec, getterDec, setterDec, propertyDec, parameterDec, tooFewArguments, badReturnType, factory} from '.';
3+
4+
expectError(() => { // 1238, 1270
5+
@classDec
6+
class Test {}
7+
});
8+
expectError(() => { // 1238, 1270
9+
@classDec
10+
abstract class Test extends Base {}
11+
});
12+
13+
expectError(() => { // 1241
14+
class Test extends Base {
15+
@methodDec static foo(a: string, b: number) { return true; }
16+
}
17+
});
18+
expectError(() => { // 1241
19+
class Test extends Base {
20+
@methodDec foo() {}
21+
}
22+
});
23+
expectError(() => { // 1241
24+
class Test {
25+
@methodDec foo(a: string, b: number) { return true; }
26+
}
27+
});
28+
expectError(() => { // 1249
29+
class Test extends Base {
30+
@methodDec override dummy(a: string, b: number): boolean
31+
dummy(): void
32+
dummy(a?: string, b?: number) : boolean|void {}
33+
}
34+
});
35+
36+
expectError(() => { // 1241
37+
class Test extends Base {
38+
@getterDec static get foo() { return 42; }
39+
}
40+
});
41+
expectError(() => { // 1241
42+
class Test extends Base {
43+
@getterDec get foo() { return "bar"; }
44+
}
45+
});
46+
expectError(() => { // 1241
47+
class Test {
48+
@getterDec get foo() { return 42; }
49+
}
50+
});
51+
52+
expectError(() => { // 1241
53+
class Test extends Base {
54+
@setterDec static set foo(value: number) {}
55+
}
56+
});
57+
expectError(() => { // 1241
58+
class Test extends Base {
59+
@setterDec static set foo(value: string) {}
60+
}
61+
});
62+
expectError(() => { // 1241
63+
class Test {
64+
@setterDec set foo(value: number) {}
65+
}
66+
});
67+
68+
expectError(() => { // 1240
69+
class Test extends Base {
70+
@propertyDec static foo = 42;
71+
}
72+
});
73+
expectError(() => { // 1240
74+
class Test {
75+
@propertyDec foo = 42;
76+
}
77+
});
78+
79+
expectError(() => { // 1239
80+
class Test extends Base {
81+
static foo(@parameterDec a: number) {}
82+
}
83+
});
84+
expectError(() => { // 1241
85+
class Test {
86+
foo(@parameterDec a: number) {}
87+
}
88+
});
89+
90+
91+
expectError(() => {
92+
class Test {
93+
@tooFewArguments foo() {}
94+
}
95+
});
96+
expectError(() => { // 1271
97+
class Test extends Base {
98+
@badReturnType accessor foo = 42;
99+
}
100+
})
101+
102+
expectError(() => {
103+
class Test {
104+
@factory("bar") foo(a: number) {}
105+
}
106+
});
107+
expectError(() => {
108+
class Test {
109+
@factory foo(a: number) {}
110+
}
111+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"name": "foo",
3+
"tsd": {
4+
"compilerOptions": {
5+
"experimentalDecorators": true
6+
}
7+
}
8+
}

0 commit comments

Comments
 (0)
Please sign in to comment.