From db9462ff7a798bf4040bed3dd9cf94a0de22a86b Mon Sep 17 00:00:00 2001 From: Jake 'Sid' Smith Date: Fri, 6 Mar 2020 18:02:57 +0000 Subject: [PATCH 1/8] Add type assertions for call expressions --- assertions/fail.ts | 2 ++ assertions/pass.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/assertions/fail.ts b/assertions/fail.ts index dab2046..421e30c 100644 --- a/assertions/fail.ts +++ b/assertions/fail.ts @@ -15,6 +15,8 @@ export const abc = {} as ABC | null; // @type: ABC | null export const result = removeNull(abc); // @type: ABC | null +removeNull(abc); // @type: ABC | null + export const c = abc?.a?.b.c; // @type string | number export const d = abc?.d; // @type ['a', 'b', 'c'] | undefined diff --git a/assertions/pass.ts b/assertions/pass.ts index d6628ab..113f98d 100644 --- a/assertions/pass.ts +++ b/assertions/pass.ts @@ -15,6 +15,8 @@ export const abc = {} as ABC | null; // @type: ABC | null export const result = removeNull(abc); // @type: ABC +removeNull(abc); // @type: ABC + export const c = abc?.a?.b.c; // @type string | number | undefined export const d = abc?.d; // @type ["a", "b", "c"] | undefined From a541097bd4742cb608ee2dc308664de9fadda9a3 Mon Sep 17 00:00:00 2001 From: Jake 'Sid' Smith Date: Fri, 6 Mar 2020 18:03:39 +0000 Subject: [PATCH 2/8] Assert return type of call expressions --- src/assert.ts | 41 +++++++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/src/assert.ts b/src/assert.ts index 91049c0..5241ca1 100644 --- a/src/assert.ts +++ b/src/assert.ts @@ -120,21 +120,46 @@ const assert = (tree: Tree) => { const result = MATCHES_TRAILING_COMMENT.exec(line); - if (result && ts.isVariableDeclaration(node)) { + if (result) { commentsChecked += 1; + const comment = result[1]; const lineNumber = lineIndex + 1; - const symbol = checker.getSymbolAtLocation(node.name); + const fileLine = `${file.fileName}:${lineNumber}: `; + + if (ts.isVariableDeclaration(node)) { + const symbol = checker.getSymbolAtLocation(node.name); + const variableName = node.name.getText(); - if (symbol) { - const typeNode = checker.getTypeOfSymbolAtLocation(symbol, node); - const type = checker.typeToString(typeNode); + if (symbol) { + const typeNode = checker.getTypeOfSymbolAtLocation(symbol, node); + const type = checker.typeToString(typeNode); + + if (type !== comment) { + errors.push( + `${fileLine}Type of "${variableName}" - ${type} did not match type comment ${comment}` + ); + } + } - if (type !== comment) { - errors.push( - `${file.fileName}:${lineNumber}: Type ${type} did not match type comment ${comment}` + return; + } else if (ts.isCallExpression(node)) { + const signature = checker.getResolvedSignature(node); + const functionName = node.expression.getText(); + + if (signature) { + const type = checker.typeToString( + checker.getReturnTypeOfSignature(signature) ); + + if (type !== comment) { + errors.push( + `${fileLine}Return type of "${functionName}" - ${type} did not match type comment ${comment}` + ); + } } + + return; } } From 16c3504585d5e1228b816abfa76d14b5c98fe2d5 Mon Sep 17 00:00:00 2001 From: Jake 'Sid' Smith Date: Fri, 6 Mar 2020 18:09:35 +0000 Subject: [PATCH 3/8] Ensure all type assertions fail --- assertions/fail.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assertions/fail.ts b/assertions/fail.ts index 421e30c..02640bc 100644 --- a/assertions/fail.ts +++ b/assertions/fail.ts @@ -11,7 +11,7 @@ interface ABC { d: ['a', 'b', 'c']; } -export const abc = {} as ABC | null; // @type: ABC | null +export const abc = {} as ABC | null; // @type: ABC export const result = removeNull(abc); // @type: ABC | null From 426d315a9687b96093323610e7ceac2824b184e9 Mon Sep 17 00:00:00 2001 From: Jake 'Sid' Smith Date: Fri, 6 Mar 2020 18:11:01 +0000 Subject: [PATCH 4/8] Assert return type of new expressions --- assertions/fail.ts | 11 +++++++++++ assertions/pass.ts | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/assertions/fail.ts b/assertions/fail.ts index 02640bc..7373f6e 100644 --- a/assertions/fail.ts +++ b/assertions/fail.ts @@ -20,3 +20,14 @@ removeNull(abc); // @type: ABC | null export const c = abc?.a?.b.c; // @type string | number export const d = abc?.d; // @type ['a', 'b', 'c'] | undefined + +class MyClass { + public input: T; + + public constructor(input: T) { + this.input = input; + } +} + +// tslint:disable-next-line:no-unused-expression +new MyClass(abc); // @type: MyClass diff --git a/assertions/pass.ts b/assertions/pass.ts index 113f98d..6a51693 100644 --- a/assertions/pass.ts +++ b/assertions/pass.ts @@ -20,3 +20,14 @@ removeNull(abc); // @type: ABC export const c = abc?.a?.b.c; // @type string | number | undefined export const d = abc?.d; // @type ["a", "b", "c"] | undefined + +class MyClass { + public input: T; + + public constructor(input: T) { + this.input = input; + } +} + +// tslint:disable-next-line:no-unused-expression +new MyClass(abc); // @type: MyClass From 9fffa123911cc41537519fef203b9b420c39beeb Mon Sep 17 00:00:00 2001 From: Jake 'Sid' Smith Date: Fri, 6 Mar 2020 18:11:12 +0000 Subject: [PATCH 5/8] Allow asserting new expressions --- src/assert.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/assert.ts b/src/assert.ts index 5241ca1..e966915 100644 --- a/src/assert.ts +++ b/src/assert.ts @@ -143,7 +143,7 @@ const assert = (tree: Tree) => { } return; - } else if (ts.isCallExpression(node)) { + } else if (ts.isCallExpression(node) || ts.isNewExpression(node)) { const signature = checker.getResolvedSignature(node); const functionName = node.expression.getText(); From 8964399898a9eaa4a4acbfad469ce8d22e624936 Mon Sep 17 00:00:00 2001 From: Jake 'Sid' Smith Date: Fri, 6 Mar 2020 19:02:48 +0000 Subject: [PATCH 6/8] Put new relativeFileName in logs --- src/assert.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/assert.ts b/src/assert.ts index d2f2729..1ade5b4 100644 --- a/src/assert.ts +++ b/src/assert.ts @@ -137,7 +137,7 @@ const assert = (tree: Tree) => { const comment = result[1]; const lineNumber = lineIndex + 1; - const fileLine = `${file.fileName}:${lineNumber}: `; + const fileLine = `${relativeFileName}:${lineNumber}: `; if (ts.isVariableDeclaration(node)) { const symbol = checker.getSymbolAtLocation(node.name); From eadba7dd5108693fc01a79e537427edd6fe584f8 Mon Sep 17 00:00:00 2001 From: Jake 'Sid' Smith Date: Fri, 6 Mar 2020 19:10:46 +0000 Subject: [PATCH 7/8] Add basic examples of all assertions to readme --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index 7e6049a..50eee15 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,19 @@ Simply add a comment with the following structure to the end of any variable dec // @type: ExpectedTypeHere ``` +Basic examples: + +```ts +// Assert variable types +const myNumber = 1; // @type: number + +// Assert return type of function +sendMessage('Hello'); // @type: Promise + +// Assert type of class instance +new MyClass(abc); // @type: MyClass +``` + Example in tests: ```ts From 43beb0c505ba4f32f3e1ba55832118fec569544e Mon Sep 17 00:00:00 2001 From: Jake 'Sid' Smith Date: Fri, 6 Mar 2020 19:12:25 +0000 Subject: [PATCH 8/8] 0.2.2 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9c60a4f..b8d6164 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@jakesidsmith/tsassert", - "version": "0.2.1", + "version": "0.2.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 35d6da0..da232b5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@jakesidsmith/tsassert", - "version": "0.2.1", + "version": "0.2.2", "description": "Check TypeScript types against assertion comments", "publishConfig": { "access": "public"