-
-
Notifications
You must be signed in to change notification settings - Fork 66
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add expectNotType assertion - fixes #56
- Loading branch information
1 parent
67ae75b
commit 861db08
Showing
14 changed files
with
165 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
import {CallExpression} from '../../../../libraries/typescript/lib/typescript'; | ||
import {TypeChecker} from '../../entities/typescript'; | ||
import {Diagnostic} from '../../interfaces'; | ||
import {makeDiagnostic} from '../../utils'; | ||
|
||
/** | ||
* Verifies that the argument of the assertion is identical to the generic type of the assertion. | ||
* | ||
* @param checker - The TypeScript type checker. | ||
* @param nodes - The `expectType` AST nodes. | ||
* @return List of custom diagnostics. | ||
*/ | ||
export const isIdentical = (checker: TypeChecker, nodes: Set<CallExpression>): Diagnostic[] => { | ||
const diagnostics: Diagnostic[] = []; | ||
|
||
if (!nodes) { | ||
return diagnostics; | ||
} | ||
|
||
for (const node of nodes) { | ||
if (!node.typeArguments) { | ||
// Skip if the node does not have generics | ||
continue; | ||
} | ||
|
||
// Retrieve the type to be expected. This is the type inside the generic. | ||
const expectedType = checker.getTypeFromTypeNode(node.typeArguments[0]); | ||
|
||
// Retrieve the argument type. This is the type to be checked. | ||
const argumentType = checker.getTypeAtLocation(node.arguments[0]); | ||
|
||
if (!checker.isTypeAssignableTo(argumentType, expectedType)) { | ||
// The argument type is not assignable to the expected type. TypeScript will catch this for us. | ||
continue; | ||
} | ||
|
||
if (!checker.isTypeAssignableTo(expectedType, argumentType)) { | ||
/** | ||
* The expected type is not assignable to the argument type, but the argument type is | ||
* assignable to the expected type. This means our type is too wide. | ||
*/ | ||
diagnostics.push(makeDiagnostic(node, `Parameter type \`${checker.typeToString(expectedType)}\` is declared too wide for argument type \`${checker.typeToString(argumentType)}\`.`)); | ||
} else if (!checker.isTypeIdenticalTo(expectedType, argumentType)) { | ||
/** | ||
* The expected type and argument type are assignable in both directions. We still have to check | ||
* if the types are identical. See https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md#3.11.2. | ||
*/ | ||
diagnostics.push(makeDiagnostic(node, `Parameter type \`${checker.typeToString(expectedType)}\` is not identical to argument type \`${checker.typeToString(argumentType)}\`.`)); | ||
} | ||
} | ||
|
||
return diagnostics; | ||
}; | ||
|
||
/** | ||
* Verifies that the argument of the assertion is not identical to the generic type of the assertion. | ||
* | ||
* @param checker - The TypeScript type checker. | ||
* @param nodes - The `expectType` AST nodes. | ||
* @return List of custom diagnostics. | ||
*/ | ||
export const isNotIdentical = (checker: TypeChecker, nodes: Set<CallExpression>): Diagnostic[] => { | ||
const diagnostics: Diagnostic[] = []; | ||
|
||
if (!nodes) { | ||
return diagnostics; | ||
} | ||
|
||
for (const node of nodes) { | ||
if (!node.typeArguments) { | ||
// Skip if the node does not have generics | ||
continue; | ||
} | ||
|
||
// Retrieve the type to be expected. This is the type inside the generic. | ||
const expectedType = checker.getTypeFromTypeNode(node.typeArguments[0]); | ||
const argumentType = checker.getTypeAtLocation(node.arguments[0]); | ||
|
||
if (checker.isTypeIdenticalTo(expectedType, argumentType)) { | ||
diagnostics.push(makeDiagnostic(node, `Parameter type \`${checker.typeToString(expectedType)}\` is identical to argument type \`${checker.typeToString(argumentType)}\`.`)); | ||
} | ||
} | ||
|
||
return diagnostics; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
export {Handler} from './handler'; | ||
|
||
// Handlers | ||
export {strictAssertion} from './strict-assertion'; | ||
export {isIdentical, isNotIdentical} from './identicality'; | ||
export {isNotAssignable} from './assignability'; | ||
export {expectDeprecated, expectNotDeprecated} from './expect-deprecated'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
declare const concat: { | ||
(foo: string, bar: string): string; | ||
(foo: number, bar: number): number; | ||
}; | ||
|
||
export default concat; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
module.exports.default = (foo, bar) => { | ||
return foo + bar; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import {expectType} from '../../../..'; | ||
import concat from '.'; | ||
|
||
expectType<string>(concat('foo', 'bar')); | ||
expectType<number>(concat(1, 2)); | ||
|
||
expectType<any>(concat(1, 2)); | ||
expectType<string | number>(concat('unicorn', 'rainbow')); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"name": "foo" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
declare const concat: { | ||
(foo: string, bar: string): string; | ||
(foo: number, bar: number): number; | ||
}; | ||
|
||
export default concat; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
module.exports.default = (foo, bar) => { | ||
return foo + bar; | ||
}; |
7 changes: 7 additions & 0 deletions
7
source/test/fixtures/identicality/not-identical/index.test-d.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import {expectNotType} from '../../../..'; | ||
import concat from '.'; | ||
|
||
expectNotType<number>(concat('foo', 'bar')); | ||
expectNotType<string | number>(concat('foo', 'bar')); | ||
|
||
expectNotType<string>(concat('unicorn', 'rainbow')); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"name": "foo" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import * as path from 'path'; | ||
import test from 'ava'; | ||
import {verify} from './fixtures/utils'; | ||
import tsd from '..'; | ||
|
||
test('identical', async t => { | ||
const diagnostics = await tsd({cwd: path.join(__dirname, 'fixtures/identicality/identical')}); | ||
|
||
verify(t, diagnostics, [ | ||
[7, 0, 'error', 'Parameter type `any` is declared too wide for argument type `number`.'], | ||
[8, 0, 'error', 'Parameter type `string | number` is declared too wide for argument type `string`.'] | ||
]); | ||
}); | ||
|
||
test('not identical', async t => { | ||
const diagnostics = await tsd({cwd: path.join(__dirname, 'fixtures/identicality/not-identical')}); | ||
|
||
verify(t, diagnostics, [ | ||
[7, 0, 'error', 'Parameter type `string` is identical to argument type `string`.'] | ||
]); | ||
}); |