Skip to content

Commit b68c37c

Browse files
Check if types are identical in strict assertions - fixes #43
1 parent 30860fe commit b68c37c

File tree

7 files changed

+40
-12
lines changed

7 files changed

+40
-12
lines changed

libraries/typescript/lib/typescript.d.ts

+20-7
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
/*! *****************************************************************************
2-
Copyright (c) Microsoft Corporation. All rights reserved.
2+
Copyright (c) Microsoft Corporation. All rights reserved.
33
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
44
this file except in compliance with the License. You may obtain a copy of the
5-
License at http://www.apache.org/licenses/LICENSE-2.0
6-
5+
License at http://www.apache.org/licenses/LICENSE-2.0
6+
77
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
88
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
9-
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
10-
MERCHANTABLITY OR NON-INFRINGEMENT.
11-
9+
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
10+
MERCHANTABLITY OR NON-INFRINGEMENT.
11+
1212
See the Apache Version 2.0 License for specific language governing permissions
1313
and limitations under the License.
1414
***************************************************************************** */
@@ -1995,6 +1995,19 @@ declare namespace ts {
19951995
getAugmentedPropertiesOfType(type: Type): Symbol[];
19961996
getRootSymbols(symbol: Symbol): ReadonlyArray<Symbol>;
19971997
getContextualType(node: Expression): Type | undefined;
1998+
/**
1999+
* Two types are considered identical when
2000+
* - they are both the `any` type,
2001+
* - they are the same primitive type,
2002+
* - they are the same type parameter,
2003+
* - they are union types with identical sets of constituent types, or
2004+
* - they are intersection types with identical sets of constituent types, or
2005+
* - they are object types with identical sets of members.
2006+
*
2007+
* This relationship is bidirectional.
2008+
* See [here](https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md#3.11.2) for more information.
2009+
*/
2010+
isIdenticalTo(a: Type, b: Type): boolean;
19982011
/**
19992012
* Checks if type `a` is assignable to type `b`.
20002013
*/
@@ -5803,4 +5816,4 @@ declare namespace ts {
58035816
function transform<T extends Node>(source: T | T[], transformers: TransformerFactory<T>[], compilerOptions?: CompilerOptions): TransformationResult<T>;
58045817
}
58055818

5806-
export = ts;
5819+
export = ts;

libraries/typescript/lib/typescript.js

+1
Original file line numberDiff line numberDiff line change
@@ -32344,6 +32344,7 @@ var ts;
3234432344
var parsed = ts.getParseTreeNode(node, ts.isFunctionLike);
3234532345
return parsed ? isImplementationOfOverload(parsed) : undefined;
3234632346
},
32347+
isIdenticalTo: isTypeIdenticalTo,
3234732348
isAssignableTo: isTypeAssignableTo,
3234832349
getImmediateAliasedSymbol: getImmediateAliasedSymbol,
3234932350
getAliasedSymbol: resolveAlias,

readme.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ These options will be overridden if a `tsconfig.json` file is found in your proj
142142

143143
### expectType&lt;T&gt;(value)
144144

145-
Check if a value is of a specific type.
145+
Check that `value` is identical to type `T`.
146146

147147
### expectError(function)
148148

source/lib/assertions/assert.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
2-
* Assert the type of the value.
2+
* Check that `value` is identical to type `T`.
33
*
4-
* @param value - Value that should be type checked.
4+
* @param value - Value that should be identical to type `T`.
55
*/
66
// @ts-ignore
77
export const expectType = <T>(value: T) => { // tslint:disable-line:no-unused

source/lib/assertions/handlers/strict-assertion.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,18 @@ export const strictAssertion = (checker: TypeChecker, nodes: Set<CallExpression>
3131
continue;
3232
}
3333

34-
if (!checker.isAssignableTo(expectedType, argumentType)) { // tslint:disable-line:early-exit
34+
if (!checker.isAssignableTo(expectedType, argumentType)) {
3535
/**
3636
* The expected type is not assignable to the argument type, but the argument type is
3737
* assignable to the expected type. This means our type is too wide.
3838
*/
3939
diagnostics.push(makeDiagnostic(node, `Parameter type \`${checker.typeToString(expectedType)}\` is declared too wide for argument type \`${checker.typeToString(argumentType)}\`.`));
40+
} else if (!checker.isIdenticalTo(expectedType, argumentType)) {
41+
/**
42+
* The expected type and argument type are assignable in both directions. We still have to check
43+
* if the types are identical. See https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md#3.11.2.
44+
*/
45+
diagnostics.push(makeDiagnostic(node, `Parameter type \`${checker.typeToString(expectedType)}\` is not identical to argument type \`${checker.typeToString(argumentType)}\`.`));
4046
}
4147
}
4248

source/test/fixtures/strict-types/loose/index.test-d.ts

+6
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,9 @@ abstract class Foo<T> {
2828
expectType<Foo<string | Foo<string | number>> | Foo<Date> | Foo<Symbol>>(
2929
one<Foo<Date> | Foo<Symbol> | Foo<Foo<number> | string>>()
3030
);
31+
32+
expectType<string | number>(one<any>());
33+
34+
expectType<Observable<string | number> | Observable<string | number | boolean> | Observable<any>>(
35+
one<Observable<string | number> | Observable<string | number | boolean> | Observable<string>>()
36+
);

source/test/test.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,9 @@ test('loose types', async t => {
237237
[14, 0, 'error', 'Parameter type `Promise<string | number>` is declared too wide for argument type `Promise<number>`.'],
238238
[16, 0, 'error', 'Parameter type `Observable<string | number>` is declared too wide for argument type `Observable<string>`.'],
239239
[20, 0, 'error', 'Parameter type `Observable<string | number> | Observable<string | number | boolean>` is declared too wide for argument type `Observable<string | number> | Observable<string>`.'],
240-
[28, 0, 'error', 'Parameter type `Foo<string | Foo<string | number>> | Foo<Date> | Foo<Symbol>` is declared too wide for argument type `Foo<Date> | Foo<Symbol> | Foo<string | Foo<number>>`.']
240+
[28, 0, 'error', 'Parameter type `Foo<string | Foo<string | number>> | Foo<Date> | Foo<Symbol>` is declared too wide for argument type `Foo<Date> | Foo<Symbol> | Foo<string | Foo<number>>`.'],
241+
[32, 0, 'error', 'Parameter type `string | number` is not identical to argument type `any`.'],
242+
[34, 0, 'error', 'Parameter type `Observable<string | number> | Observable<any> | Observable<string | number | boolean>` is not identical to argument type `Observable<string | number> | Observable<string> | Observable<string | number | boolean>`.']
241243
]);
242244
});
243245

0 commit comments

Comments
 (0)