diff --git a/CHANGELOG.md b/CHANGELOG.md index b4cdd3420be1..e1ef3104cc39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### Features - `[jest-core]` Enable testResultsProcessor to be async ([#13343](https://github.com/facebook/jest/pull/13343)) +- `[expect, @jest/expect-utils]` Allow `isA` utility to take a type argument ([#13355](https://github.com/facebook/jest/pull/13355)) ### Fixes diff --git a/packages/expect-utils/__typetests__/tsconfig.json b/packages/expect-utils/__typetests__/tsconfig.json new file mode 100644 index 000000000000..165ba1343021 --- /dev/null +++ b/packages/expect-utils/__typetests__/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "composite": false, + "noUnusedLocals": false, + "noUnusedParameters": false, + "skipLibCheck": true, + + "types": [] + }, + "include": ["./**/*"] +} diff --git a/packages/expect-utils/__typetests__/utils.test.ts b/packages/expect-utils/__typetests__/utils.test.ts new file mode 100644 index 000000000000..bcb580cb28a6 --- /dev/null +++ b/packages/expect-utils/__typetests__/utils.test.ts @@ -0,0 +1,38 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import {expectType} from 'tsd-lite'; +import {isA} from '@jest/expect-utils'; + +// isA + +expectType(isA('String', 'default')); +expectType(isA('Number', 123)); + +const sample = {} as unknown; + +expectType(sample); + +if (isA('String', sample)) { + expectType(sample); +} + +if (isA('String', sample)) { + expectType(sample); +} + +if (isA('Number', sample)) { + expectType(sample); +} + +if (isA>('Map', sample)) { + expectType>(sample); +} + +if (isA>('Set', sample)) { + expectType>(sample); +} diff --git a/packages/expect-utils/package.json b/packages/expect-utils/package.json index db9792da38e9..7cb81f6a7f84 100644 --- a/packages/expect-utils/package.json +++ b/packages/expect-utils/package.json @@ -20,8 +20,10 @@ "jest-get-type": "workspace:^" }, "devDependencies": { + "@tsd/typescript": "~4.8.2", "immutable": "^4.0.0", - "jest-matcher-utils": "workspace:^" + "jest-matcher-utils": "workspace:^", + "tsd-lite": "^0.6.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" diff --git a/packages/expect-utils/src/jasmineUtils.ts b/packages/expect-utils/src/jasmineUtils.ts index fc7a767a82d6..3fbdafd0f6c5 100644 --- a/packages/expect-utils/src/jasmineUtils.ts +++ b/packages/expect-utils/src/jasmineUtils.ts @@ -224,7 +224,7 @@ function hasKey(obj: any, key: string) { return Object.prototype.hasOwnProperty.call(obj, key); } -export function isA(typeName: string, value: unknown) { +export function isA(typeName: string, value: unknown): value is T { return Object.prototype.toString.apply(value) === '[object ' + typeName + ']'; } @@ -262,10 +262,7 @@ export function isImmutableUnorderedSet(maybeSet: any) { } export function isImmutableList(maybeList: any) { - return !!( - maybeList && - maybeList[IS_LIST_SENTINEL] - ); + return !!(maybeList && maybeList[IS_LIST_SENTINEL]); } export function isImmutableOrderedKeyed(maybeKeyed: any) { @@ -276,7 +273,6 @@ export function isImmutableOrderedKeyed(maybeKeyed: any) { ); } - export function isImmutableOrderedSet(maybeSet: any) { return !!( maybeSet && @@ -286,8 +282,5 @@ export function isImmutableOrderedSet(maybeSet: any) { } export function isImmutableRecord(maybeSet: any) { - return !!( - maybeSet && - maybeSet[IS_RECORD_SYMBOL] - ); -} \ No newline at end of file + return !!(maybeSet && maybeSet[IS_RECORD_SYMBOL]); +} diff --git a/packages/expect-utils/src/utils.ts b/packages/expect-utils/src/utils.ts index daef65bfc7ca..32ce987ee92c 100644 --- a/packages/expect-utils/src/utils.ts +++ b/packages/expect-utils/src/utils.ts @@ -184,7 +184,7 @@ export const iterableEquality = ( if (a.size !== undefined) { if (a.size !== b.size) { return false; - } else if (isA('Set', a) || isImmutableUnorderedSet(a)) { + } else if (isA>('Set', a) || isImmutableUnorderedSet(a)) { let allFound = true; for (const aValue of a) { if (!b.has(aValue)) { @@ -206,7 +206,10 @@ export const iterableEquality = ( aStack.pop(); bStack.pop(); return allFound; - } else if (isA('Map', a) || isImmutableUnorderedKeyed(a)) { + } else if ( + isA>('Map', a) || + isImmutableUnorderedKeyed(a) + ) { let allFound = true; for (const aEntry of a) { if ( diff --git a/packages/expect/src/asymmetricMatchers.ts b/packages/expect/src/asymmetricMatchers.ts index 41516ba7d364..dd62850eea2f 100644 --- a/packages/expect/src/asymmetricMatchers.ts +++ b/packages/expect/src/asymmetricMatchers.ts @@ -185,7 +185,7 @@ class ArrayContaining extends AsymmetricMatcher> { super(sample, inverse); } - asymmetricMatch(other: Array) { + asymmetricMatch(other: unknown) { if (!Array.isArray(this.sample)) { throw new Error( `You must provide an array to ${this.toString()}, not '${typeof this @@ -257,8 +257,8 @@ class StringContaining extends AsymmetricMatcher { super(sample, inverse); } - asymmetricMatch(other: string) { - const result = isA('String', other) && other.includes(this.sample); + asymmetricMatch(other: unknown) { + const result = isA('String', other) && other.includes(this.sample); return this.inverse ? !result : result; } @@ -280,8 +280,8 @@ class StringMatching extends AsymmetricMatcher { super(new RegExp(sample), inverse); } - asymmetricMatch(other: string) { - const result = isA('String', other) && this.sample.test(other); + asymmetricMatch(other: unknown) { + const result = isA('String', other) && this.sample.test(other); return this.inverse ? !result : result; } @@ -297,6 +297,7 @@ class StringMatching extends AsymmetricMatcher { class CloseTo extends AsymmetricMatcher { private precision: number; + constructor(sample: number, precision = 2, inverse = false) { if (!isA('Number', sample)) { throw new Error('Expected is not a Number'); @@ -311,8 +312,8 @@ class CloseTo extends AsymmetricMatcher { this.precision = precision; } - asymmetricMatch(other: number) { - if (!isA('Number', other)) { + asymmetricMatch(other: unknown) { + if (!isA('Number', other)) { return false; } let result = false; diff --git a/yarn.lock b/yarn.lock index ed6b150b11a3..5e4330a38c9b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2695,9 +2695,11 @@ __metadata: version: 0.0.0-use.local resolution: "@jest/expect-utils@workspace:packages/expect-utils" dependencies: + "@tsd/typescript": ~4.8.2 immutable: ^4.0.0 jest-get-type: "workspace:^" jest-matcher-utils: "workspace:^" + tsd-lite: ^0.6.0 languageName: unknown linkType: soft