8000 Added equals, createEquals, assertEquals, createAssertEquals function… · SebastienGllmt/typescript-is@daf8fb8 · GitHub
[go: up one dir, main page]

Skip to content

Commit daf8fb8

Browse files
committed
Added equals, createEquals, assertEquals, createAssertEquals functions, added unit test issue-22
1 parent f158499 commit daf8fb8

25 files changed

+1056
-869
lines changed

index.d.ts

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
/**
2-
* Checks if the given argument matches the given type-argument.
2+
* Checks if the given argument is assignable to the given type-argument.
33
*
44
* @param object object whose type needs to be checked.
5-
* @returns `true` if `object` matches `T`, F438 false otherwise.
5+
* @returns `true` if `object` is assignable to `T`, false otherwise.
66
* @example
77
```
88
is<number>(42); // -> true
@@ -26,8 +26,38 @@ export function is<T>(object: any): object is T;
2626
export function createIs<T>(): (object: any) => object is T;
2727

2828
/**
29-
* Asserts the given argument to be of the given type-argument.
30-
* If the given argument does not match the given type-argument, an error will be thrown.
29+
* Checks if the given argument is assignable to the given type-argument and vice versa.
30+
* Superfluous properties will cause the validation to fail.
31+
*
32+
* @param object object whose type needs to be checked.
33+
* @returns `true` if `object` is assignable to `T` and if `T` is "assignable" to `object`, false otherwise.
34+
* @example
35+
```
36+
is<{ foo: string }>({}); // -> false
37+
is<{ foo: string }>({ foo: 'bar' }); // -> true
38+
is<{ foo: string }>({ foo: 'bar', baz: 'qux' }); // -> false
39+
```
40+
*/
41+
export function equals<T>(object: any): object is T;
42+
43+
/**
44+
* Creates a function similar to `equals<T>` that can be invoked at a later point.
45+
*
46+
* This is useful, for example, if you want to re-use the function multiple times.
47+
*
48+
* @example
49+
```
50+
const checkObject = createEquals<{ foo: string }>();
51+
checkObject({}); // -> false
52+
checkObject({ foo: 'bar' }); // -> true
53+
checkObject({ foo: 'bar', baz: 'qux' }); // -> false
54+
```
55+
*/
56+
export function createEquals<T>(): (object: any) => object is T;
57+
58+
/**
59+
* Asserts the given argument to be assignable to the given type-argument.
60+
* If the given argument is not assignable to the given type-argument, an error will be thrown.
3161
*
3262
* @param object object whose type will be asserted.
3363
* @returns the given `object`, or an error is thrown if validation failed.
@@ -53,6 +83,36 @@ export function assertType<T>(object: any): T;
5383
*/
5484
export function createAssertType<T>(): (object: any) => T;
5585

86+
/**
87+
* Asserts the given argument to be assignable to the given type-argument and vice versa.
88+
* If the given argument is not assignable to the given type-argument, an error will be thrown.
89+
* If the given type-argument is not assignable to the given argument, an error will be thrown.
90+
* Superfluous properties will cause the validation to fail.
91+
*
92+
* @param object object whose type will be asserted.
93+
* @returns the given `object`, or an error is thrown if validation failed.
94+
* @example
95+
```
96+
const safeObject = assertEquals<{ foo: string }>({ foo: 'bar' }); // safeObject === { foo: 'bar' }, code continues
97+
assertEquals<{ foo: string }>({ foo: 'bar', baz: 'qux' }); // throws an error
98+
```
99+
*/
100+
export function assertEquals<T>(object: any): T;
101+
102+
/**
103+
* Creates a function similar to `assertEquals<T>` that can be invoked at a later point.
104+
*
105+
* This is useful, for example, if you want to re-use the function multiple times.
106+
*
107+
* @example
108+
```
109+
const assertObject = createAssertEquals<{ foo: string }>();
110+
const safeObject = assertObject({ foo: 'bar' }); // safeObject === { foo: 'bar' }, code continues
111+
assertObject({ foo: 'bar', baz: 'qux' }); // throws an error
112+
```
113+
*/
114+
export function createAssertEquals<T>(): (object: any) => T;
115+
56116
/**
57117
* Options for the `AssertType` decorator.
58118
*/

index.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,16 @@ function createAssertType(getErrorMessage) {
6969
return (obj) => assertType(obj, getErrorMessage);
7070
}
7171

72-
module.exports = { is, assertType, createIs, createAssertType, AssertType, ValidateClass, TypeGuardError };
72+
module.exports = {
73+
is,
74+
assertType,
75+
createIs,
76+
createAssertType,
77+
equals: is,
78+
createEquals: createIs,
79+
assertEquals: assertType,
80+
createAssertEquals: createAssertType,
81+
AssertType,
82+
ValidateClass,
83+
TypeGuardError
84+
};

src/transform-inline/transform-node.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,22 @@ export function transformNode(node: ts.Node, visitorContext: PartialVisitorConte
9898
&& node.typeArguments !== undefined
9999
&& node.typeArguments.length === 1
100100
) {
101+
const name = visitorContext.checker.getTypeAtLocation(signature.declaration).symbol.name;
102+
const isEquals = name === 'equals' || name === 'createEquals' || name === 'assertEquals' || name === 'createAssertEquals';
103+
101104
const typeArgument = node.typeArguments[0];
102105
const type = visitorContext.checker.getTypeFromTypeNode(typeArgument);
103-
const arrowFunction = createArrowFunction(type, false, visitorContext);
106+
const arrowFunction = createArrowFunction(
107+
type,
108+
false,
109+
{
110+
...visitorContext,
111+
options: {
112+
...visitorContext.options,
113+
disallowSuperfluousObjectProperties: isEquals || visitorContext.options.disallowSuperfluousObjectProperties
114+
}
115+
}
116+
);
104117

105118
return ts.updateCall(
106119
node,

test/case-1.ts

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,17 @@ describe('is', () => {
88
}
99

1010
it('should return true for nested objects with strings or numbers', () => {
11-
assert.strictEqual(is<Nested<string> | Nested<number>>({ value: 'foo' }), true);
12-
assert.strictEqual(is<Nested<string> | Nested<number>>({ value: 123 }), true);
11+
assert.deepStrictEqual(is<Nested<string> | Nested<number>>({ value: 'foo' }), true);
12+
assert.deepStrictEqual(is<Nested<string> | Nested<number>>({ value: 123 }), true);
1313
});
1414

1515
it('should return false for nested objects with other types', () => {
16-
assert.strictEqual(is<Nested<string> | Nested<number>>({ value: [] }), false);
17-
assert.strictEqual(is<Nested<string> | Nested<number>>({ value: {} }), false);
18-
assert.strictEqual(is<Nested<string> | Nested<number>>({ value: null }), false);
19-
assert.strictEqual(is<Nested<string> | Nested<number>>({ value: undefined }), false);
20-
assert.strictEqual(is<Nested<string> | Nested<number>>({ value: true }), false);
21-
assert.strictEqual(is<Nested<string> | Nested<number>>({ value: false }), false);
16+
assert.deepStrictEqual(is<Nested<string> | Nested<number>>({ value: [] }), false);
17+
assert.deepStrictEqual(is<Nested<string> | Nested<number>>({ value: {} }), false);
18+
assert.deepStrictEqual(is<Nested<string> | Nested<number>>({ value: null }), false);
19+
assert.deepStrictEqual(is<Nested<string> | Nested<number>>({ value: undefined }), false);
20+
assert.deepStrictEqual(is<Nested<string> | Nested<number>>({ value: true }), false);
21+
assert.deepStrictEqual(is<Nested<string> | Nested<number>>({ value: false }), false);
2222
});
2323
});
2424

@@ -38,61 +38,61 @@ describe('is', () => {
3838
}
3939

4040
it('should return false for the empty object literal', () => {
41-
assert.strictEqual(is<Foo<Bar<number>, string>>({}), false);
41+
assert.deepStrictEqual(is<Foo<Bar<number>, string>>({}), false);
4242
});
4343

4444
it('should return false for an object that only partially matches', () => {
45-
assert.strictEqual(is<Foo<Bar<number>, string>>({ type: {}, secondItem: {}, thirdItem: {} }), false);
45+
assert.deepStrictEqual(is<Foo<Bar<number>, string>>({ type: {}, secondItem: {}, thirdItem: {} }), false);
4646
});
4747

4848
it('should return true for objects that match the interface', () => {
49-
assert.strictEqual(is<Foo<Bar<number>, string>>({ item: 'string', buz: 1, type: 'cool', secondItem: { item: 2 }, thirdItem: { item: { item: true } } }), true);
50-
assert.strictEqual(is<Foo<Bar<number>, string>>({ item: 'text', buz: 2, type: 'cool', secondItem: { item: 3 }, thirdItem: { item: { item: false } } }), true);
49+
assert.deepStrictEqual(is<Foo<Bar<number>, string>>({ item: 'string', buz: 1, type: 'cool', secondItem: { item: 2 }, thirdItem: { item: { item: true } } }), true);
50+
assert.deepStrictEqual(is<Foo<Bar<number>, string>>({ item: 'text', buz: 2, type: 'cool', secondItem: { item: 3 }, thirdItem: { item: { item: false } } }), true);
5151
});
5252
});
5353

5454
describe('is<boolean>', () => {
5555
it('should return false for numbers', () => {
56-
assert.strictEqual(is<boolean>(0), false);
57-
assert.strictEqual(is<boolean>(1), false);
56+
assert.deepStrictEqual(is<boolean>(0), false);
57+
assert.deepStrictEqual(is<boolean>(1), false);
5858
});
5959

6060
it('should return false for strings', () => {
61-
assert.strictEqual(is<boolean>(''), false);
62-
assert.strictEqual(is<boolean>('true'), false);
63-
assert.strictEqual(is<boolean>('false'), false);
61+
assert.deepStrictEqual(is<boolean>(''), false);
62+
assert.deepStrictEqual(is<boolean>('true'), false);
63+
assert.deepStrictEqual(is<boolean>('false'), false);
6464
});
6565

6666
it('should return true for booleans', () => {
67-
assert.strictEqual(is<boolean>(true), true);
68-
assert.strictEqual(is<boolean>(false), true);
67+
assert.deepStrictEqual(is<boolean>(true), true);
68+
assert.deepStrictEqual(is<boolean>(false), true);
6969
});
7070
});
7171

7272
describe('is<number>', () => {
7373
const isNumber = (object: any): object is number => is<number>(object);
7474

7575
it('should return true for numbers', () => {
76-
assert.strictEqual(isNumber(Number.NaN), true);
77-
assert.strictEqual(isNumber(Number.POSITIVE_INFINITY), true);
78-
assert.strictEqual(isNumber(Number.NEGATIVE_INFINITY), true);
79-
assert.strictEqual(isNumber(Number.MIN_VALUE), true);
80-
assert.strictEqual(isNumber(0), true);
81-
assert.strictEqual(isNumber(1), true);
82-
assert.strictEqual(isNumber(42), true);
83-
assert.strictEqual(isNumber(-1), true);
76+
assert.deepStrictEqual(isNumber(Number.NaN), true);
77+
assert.deepStrictEqual(isNumber(Number.POSITIVE_INFINITY), true);
78+
assert.deepStrictEqual(isNumber(Number.NEGATIVE_INFINITY), true);
79+
assert.deepStrictEqual(isNumber(Number.MIN_VALUE), true);
80+
assert.deepStrictEqual(isNumber(0), true);
81+
assert.deepStrictEqual(isNumber(1), true);
82+
assert.deepStrictEqual(isNumber(42), true);
83+
assert.deepStrictEqual(isNumber(-1), true);
8484
});
8585

8686
it('should return false for strings', () => {
87-
assert.strictEqual(isNumber('0'), false);
88-
assert.strictEqual(isNumber('1'), false);
89-
assert.strictEqual(isNumber('42'), false);
90-
assert.strictEqual(isNumber('-1'), false);
87+
assert.deepStrictEqual(isNumber('0'), false);
88+
assert.deepStrictEqual(isNumber('1'), false);
89+
assert.deepStrictEqual(isNumber('42'), false);
90+
assert.deepStrictEqual(isNumber('-1'), false);
9191
});
9292

9393
it('should return false for booleans', () => {
94-
assert.strictEqual(isNumber(true), false);
95-
assert.strictEqual(isNumber(false), false);
94+
assert.deepStrictEqual(isNumber(true), false);
95+
assert.deepStrictEqual(isNumber(false), false);
9696
});
9797
});
9898
});

test/case-10.ts

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,28 @@ describe('createIs', () => {
66
const isNumber = createIs<number>();
77

88
it('should return a function', () => {
9-
assert.strictEqual(typeof isNumber, 'function');
9+
assert.deepStrictEqual(typeof isNumber, 'function');
1010
});
1111

1212
it('should return a function that returns true for numbers', () => {
13-
assert.strictEqual(isNumber(-1), true);
14-
assert.strictEqual(isNumber(0), true);
15-
assert.strictEqual(isNumber(1), true);
16-
assert.strictEqual(isNumber(Number.NaN), true);
17-
assert.strictEqual(isNumber(Number.POSITIVE_INFINITY), true);
18-
assert.strictEqual(isNumber(Number.NEGATIVE_INFINITY), true);
19-
assert.strictEqual(isNumber(42), true);
13+
assert.deepStrictEqual(isNumber(-1), true);
14+
assert.deepStrictEqual(isNumber(0), true);
15+
assert.deepStrictEqual(isNumber(1), true);
16+
assert.deepStrictEqual(isNumber(Number.NaN), true);
17+
assert.deepStrictEqual(isNumber(Number.POSITIVE_INFINITY), true);
18+
assert.deepStrictEqual(isNumber(Number.NEGATIVE_INFINITY), true);
19+
assert.deepStrictEqual(isNumber(42), true);
2020
});
2121

2222
it('should return a function that returns false for other objects', () => {
23-
assert.strictEqual(isNumber(''), false);
24-
assert.strictEqual(isNumber('1'), false);
25-
assert.strictEqual(isNumber(true), false);
26-
assert.strictEqual(isNumber(false), false);
27-
assert.strictEqual(isNumber(undefined), false);
28-
assert.strictEqual(isNumber(null), false);
29-
assert.strictEqual(isNumber({}), false);
30-
assert.strictEqual(isNumber([]), false);
23+
assert.deepStrictEqual(isNumber(''), false);
24+
assert.deepStrictEqual(isNumber('1'), false);
25+
assert.deepStrictEqual(isNumber(true), false);
26+
assert.deepStrictEqual(isNumber(false), false);
27+
assert.deepStrictEqual(isNumber(undefined), false);
28+
assert.deepStrictEqual(isNumber(null), false);
29+
assert.deepStrictEqual(isNumber({}), false);
30+
assert.deepStrictEqual(isNumber([]), false);
3131
});
3232
});
3333
});
@@ -38,15 +38,15 @@ describe('createAssertType', () => {
3838
const assertNumber = createAssertType<number>();
3939

4040
it('should return a function', () => {
41-
assert.strictEqual(typeof assertNumber, 'function');
41+
assert.deepStrictEqual(typeof assertNumber, 'function');
4242
});
4343

4444
it('should return a function that returns the numbers passed to it', () => {
45-
assert.strictEqual(assertNumber(-1), -1);
46-
assert.strictEqual(assertNumber(0), 0);
47-
assert.strictEqual(assertNumber(1), 1);
48-
assert.strictEqual(Number.isNaN(assertNumber(Number.NaN)), true);
49-
assert.strictEqual(assertNumber(Number.POSITIVE_INFINITY), Number.POSITIVE_INFINITY);
45+
assert.deepStrictEqual(assertNumber(-1), -1);
46+
assert.deepStrictEqual(assertNumber(0), 0);
47+
assert.deepStrictEqual(assertNumber(1), 1);
48+
assert.deepStrictEqual(Number.isNaN(assertNumber(Number.NaN)), true);
49+
assert.deepStrictEqual(assertNumber(Number.POSITIVE_INFINITY), Number.POSITIVE_INFINITY);
5050
});
5151

5252
it('should return a function that throws if non-numbers are passed to it', () => {

test/case-11.ts

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ describe('@ValidateClass, @AssertType', () => {
1414
const instance = new TestClass();
1515

1616
it('should pass validation for numbers', () => {
17-
assert.strictEqual(instance.testMethod(0), 0);
18-
assert.strictEqual(instance.testMethod(1), 1);
19-
assert.strictEqual(instance.testMethod(-1), -1);
20-
assert.strictEqual(instance.testMethod(42), 42);
21-
assert.strictEqual(instance.testMethod(Number.POSITIVE_INFINITY), Number.POSITIVE_INFINITY);
22-
assert.strictEqual(instance.testMethod(Number.NEGATIVE_INFINITY), Number.NEGATIVE_INFINITY);
23-
assert.strictEqual(Number.isNaN(instance.testMethod(Number.NaN)), true);
17+
assert.deepStrictEqual(instance.testMethod(0), 0);
18+
assert.deepStrictEqual(instance.testMethod(1), 1);
19+
assert.deepStrictEqual(instance.testMethod(-1), -1);
20+
assert.deepStrictEqual(instance.testMethod(42), 42);
21+
assert.deepStrictEqual(instance.testMethod(Number.POSITIVE_INFINITY), Number.POSITIVE_INFINITY);
22+
assert.deepStrictEqual(instance.testMethod(Number.NEGATIVE_INFINITY), Number.NEGATIVE_INFINITY);
23+
assert.deepStrictEqual(Number.isNaN(instance.testMethod(Number.NaN)), true);
2424
});
2525

2626
it('should throw an error for non-numbers', () => {
@@ -45,10 +45,10 @@ describe('@ValidateClass, @AssertType', () => {
4545
const instance = new TestClass();
4646

4747
it('should pass validation for strings', () => {
48-
assert.strictEqual(instance.testMethod(''), '');
49-
assert.strictEqual(instance.testMethod('42'), '42');
50-
assert.strictEqual(instance.testMethod('true'), 'true');
51-
assert.strictEqual(instance.testMethod('false'), 'false');
48+
assert.deepStrictEqual(instance.testMethod(''), '');
49+
assert.deepStrictEqual(instance.testMethod('42'), '42');
50+
assert.deepStrictEqual(instance.testMethod('true'), 'true');
51+
assert.deepStrictEqual(instance.testMethod('false'), 'false');
5252
});
5353

5454
it('should throw an error for non-strings', () => {
@@ -81,8 +81,8 @@ describe('@ValidateClass, @AssertType', () => {
8181
const instance = new TestClass();
8282

8383
it('should pass validation for booleans', () => {
84-
assert.strictEqual(instance.testMethod(true), true);
85-
assert.strictEqual(instance.testMethod(false), false);
84+
assert.deepStrictEqual(instance.testMethod(true), true);
85+
assert.deepStrictEqual(instance.testMethod(false), false);
8686
});
8787

8888
it('should throw an error for non-booleans', () => {
@@ -108,10 +108,10 @@ describe('@ValidateClass, @AssertType', () => {
108108
const instance = new TestClass();
109109

110110
it('should pass validation for strings', () => {
111-
assert.strictEqual(instance.testMethod(''), '');
112-
assert.strictEqual(instance.testMethod('0'), '0');
113-
assert.strictEqual(instance.testMethod('1'), '1');
114-
assert.strictEqual(instance.testMethod('foo'), 'foo');
111+
assert.deepStrictEqual(instance.testMethod(''), '');
112+
assert.deepStrictEqual(instance.testMethod('0'), '0');
113+
assert.deepStrictEqual(instance.testMethod('1'), '1');
114+
assert.deepStrictEqual(instance.testMethod('foo'), 'foo');
115115
});
116116

117117
it('should throw an error for non-strings with the provided custom message', () => {

0 commit comments

Comments
 (0)
0