Skip to content

Commit

Permalink
Check if types are identical in strict assertions - fixes #43
Browse files Browse the repository at this point in the history
  • Loading branch information
SamVerschueren committed Oct 18, 2019
1 parent 30860fe commit b68c37c
Show file tree
Hide file tree
Showing 7 changed files with 40 additions and 12 deletions.
27 changes: 20 additions & 7 deletions libraries/typescript/lib/typescript.d.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
Expand Down Expand Up @@ -1995,6 +1995,19 @@ declare namespace ts {
getAugmentedPropertiesOfType(type: Type): Symbol[];
getRootSymbols(symbol: Symbol): ReadonlyArray<Symbol>;
getContextualType(node: Expression): Type | undefined;
/**
* Two types are considered identical when
* - they are both the `any` type,
* - they are the same primitive type,
* - they are the same type parameter,
* - they are union types with identical sets of constituent types, or
* - they are intersection types with identical sets of constituent types, or
* - they are object types with identical sets of members.
*
* This relationship is bidirectional.
* See [here](https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md#3.11.2) for more information.
*/
isIdenticalTo(a: Type, b: Type): boolean;
/**
* Checks if type `a` is assignable to type `b`.
*/
Expand Down Expand Up @@ -5803,4 +5816,4 @@ declare namespace ts {
function transform<T extends Node>(source: T | T[], transformers: TransformerFactory<T>[], compilerOptions?: CompilerOptions): TransformationResult<T>;
}

export = ts;
export = ts;
1 change: 1 addition & 0 deletions libraries/typescript/lib/typescript.js
Original file line number Diff line number Diff line change
Expand Up @@ -32344,6 +32344,7 @@ var ts;
var parsed = ts.getParseTreeNode(node, ts.isFunctionLike);
return parsed ? isImplementationOfOverload(parsed) : undefined;
},
isIdenticalTo: isTypeIdenticalTo,
isAssignableTo: isTypeAssignableTo,
getImmediateAliasedSymbol: getImmediateAliasedSymbol,
getAliasedSymbol: resolveAlias,
Expand Down
2 changes: 1 addition & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ These options will be overridden if a `tsconfig.json` file is found in your proj

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

Check if a value is of a specific type.
Check that `value` is identical to type `T`.

### expectError(function)

Expand Down
4 changes: 2 additions & 2 deletions source/lib/assertions/assert.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* Assert the type of the value.
* Check that `value` is identical to type `T`.
*
* @param value - Value that should be type checked.
* @param value - Value that should be identical to type `T`.
*/
// @ts-ignore
export const expectType = <T>(value: T) => { // tslint:disable-line:no-unused
Expand Down
8 changes: 7 additions & 1 deletion source/lib/assertions/handlers/strict-assertion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,18 @@ export const strictAssertion = (checker: TypeChecker, nodes: Set<CallExpression>
continue;
}

if (!checker.isAssignableTo(expectedType, argumentType)) { // tslint:disable-line:early-exit
if (!checker.isAssignableTo(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.isIdenticalTo(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)}\`.`));
}
}

Expand Down
6 changes: 6 additions & 0 deletions source/test/fixtures/strict-types/loose/index.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,9 @@ abstract class Foo<T> {
expectType<Foo<string | Foo<string | number>> | Foo<Date> | Foo<Symbol>>(
one<Foo<Date> | Foo<Symbol> | Foo<Foo<number> | string>>()
);

expectType<string | number>(one<any>());

expectType<Observable<string | number> | Observable<string | number | boolean> | Observable<any>>(
one<Observable<string | number> | Observable<string | number | boolean> | Observable<string>>()
);
4 changes: 3 additions & 1 deletion source/test/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,9 @@ test('loose types', async t => {
[14, 0, 'error', 'Parameter type `Promise<string | number>` is declared too wide for argument type `Promise<number>`.'],
[16, 0, 'error', 'Parameter type `Observable<string | number>` is declared too wide for argument type `Observable<string>`.'],
[20, 0, 'error', 'Parameter type `Observable<string | number> | Observable<string | number | boolean>` is declared too wide for argument type `Observable<string | number> | Observable<string>`.'],
[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>>`.']
[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>>`.'],
[32, 0, 'error', 'Parameter type `string | number` is not identical to argument type `any`.'],
[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>`.']
]);
});

Expand Down

0 comments on commit b68c37c

Please sign in to comment.