expect-type
Advanced tools
Comparing version 0.17.0-1 to 0.17.0-2
@@ -12,13 +12,3 @@ export declare type Not<T extends boolean> = T extends true ? false : true; | ||
export declare type IsNeverOrAny<T> = Or<[IsNever<T>, IsAny<T>]>; | ||
export declare type BrandSpecial<T> = IsAny<T> extends true ? { | ||
special: true; | ||
type: 'any'; | ||
} : IsUnknown<T> extends true ? { | ||
special: true; | ||
type: 'unknown'; | ||
} : IsNever<T> extends true ? { | ||
special: true; | ||
type: 'never'; | ||
} : never; | ||
export declare type PrintType<T> = IsUnknown<T> extends true ? 'unknown' : IsNever<T> extends true ? 'never' : IsAny<T> extends true ? never : boolean extends T ? 'boolean' : T extends boolean ? `literal boolean: ${T}` : T extends string ? string extends T ? 'string' : `literal string: ${T}` : T extends number ? number extends T ? 'number' : `literal number: ${T}` : T extends null ? 'null' : T extends undefined ? 'undefined' : T extends (...args: any[]) => any ? 'function' : '...'; | ||
export declare type PrintType<T> = IsUnknown<T> extends true ? 'unknown' : IsNever<T> extends true ? 'never' : IsAny<T> extends true ? never : boolean extends T ? 'boolean' : T extends boolean ? `literal boolean: ${T}` : string extends T ? 'string' : T extends string ? `literal string: ${T}` : number extends T ? 'number' : T extends number ? `literal number: ${T}` : T extends null ? 'null' : T extends undefined ? 'undefined' : T extends (...args: any[]) => any ? 'function' : '...'; | ||
export declare type MismatchInfo<Actual, Expected> = And<[Extends<PrintType<Actual>, '...'>, Not<IsAny<Actual>>]> extends true ? { | ||
@@ -34,2 +24,5 @@ [K in keyof Actual | keyof Expected]: MismatchInfo<K extends keyof Actual ? Actual[K] : never, K extends keyof Expected ? Expected[K] : never>; | ||
* - `{ a?: string }` vs `{ a: string | undefined }` | ||
* | ||
* Note: not very performant for complex types - this should only be used when you know you need it. If doing | ||
* an equality check, it's almost always better to use `StrictEqualUsingTSInternalIdenticalToOperator`. | ||
*/ | ||
@@ -82,16 +75,22 @@ export declare type DeepBrand<T> = IsNever<T> extends true ? { | ||
declare type ReadonlyEquivalent<X, Y> = Extends<(<T>() => T extends X ? true : false), (<T>() => T extends Y ? true : false)>; | ||
/** Returns true if `L extends R`. Explicitly checks for `never` since that can give unexpected results. */ | ||
export declare type Extends<L, R> = IsNever<L> extends true ? IsNever<R> : [L] extends [R] ? true : false; | ||
export declare type ExtendsUsingBranding<L, R> = Extends<DeepBrand<L>, DeepBrand<R>>; | ||
export declare type ExtendsExcludingAnyOrNever<L, R> = IsAny<L> extends true ? IsAny<R> : Extends<L, R>; | ||
declare type StrictEqualUsingTSInternalIdenticalToOperator<L, R> = (<T>() => T extends (L & T) | T ? true : false) extends <T>() => T extends (R & T) | T ? true : false ? IsNever<L> extends IsNever<R> ? true : false : false; | ||
export declare type StrictEqualUsingBranding<Left, Right> = And<[ExtendsUsingBranding<Left, Right>, ExtendsUsingBranding<Right, Left>]>; | ||
export declare type StrictEqualUsingBranding<Left, Right> = And<[ | ||
ExtendsUsingBranding<Left, Right>, | ||
ExtendsUsingBranding<Right, Left> | ||
]>; | ||
export declare type HopefullyPerformantEqual<Left, Right> = StrictEqualUsingTSInternalIdenticalToOperator<Left, Right> extends true ? true : StrictEqualUsingBranding<Left, Right>; | ||
export declare type Params<Actual> = Actual extends (...args: infer P) => any ? P : never; | ||
export declare type ConstructorParams<Actual> = Actual extends new (...args: infer P) => any ? Actual extends new () => any ? P | [] : P : never; | ||
declare const error: unique symbol; | ||
declare const mismatch: unique symbol; | ||
declare type Mismatch = { | ||
[error]: 'mismatch'; | ||
[mismatch]: 'mismatch'; | ||
}; | ||
/** A type which should match anything passed as a value but *doesn't* match `Mismatch` - helps TypeScript select the right overload for `toEqualTypeOf` and `toMatchTypeOf`. */ | ||
declare const avalue: unique symbol; | ||
declare type AValue = { | ||
[error]?: undefined; | ||
[avalue]?: undefined; | ||
} | string | number | boolean | symbol | bigint | null | undefined | void; | ||
@@ -103,59 +102,74 @@ declare type MismatchArgs<ActualResult extends boolean, ExpectedResult extends boolean> = Eq<ActualResult, ExpectedResult> extends true ? [] : [Mismatch]; | ||
} | ||
declare const inverted: unique symbol; | ||
declare type Inverted<T> = { | ||
[error]: T; | ||
[inverted]: T; | ||
}; | ||
declare const expectNull: unique symbol; | ||
declare type ExpectNull<T> = { | ||
[error]: T; | ||
result: StrictEqualUsingTSInternalIdenticalToOperator<T, null>; | ||
[expectNull]: T; | ||
result: ExtendsExcludingAnyOrNever<T, null>; | ||
}; | ||
declare const expectUndefined: unique symbol; | ||
declare type ExpectUndefined<T> = { | ||
[error]: T; | ||
result: StrictEqualUsingTSInternalIdenticalToOperator<T, undefined>; | ||
[expectUndefined]: T; | ||
result: ExtendsExcludingAnyOrNever<T, undefined>; | ||
}; | ||
declare const expectNumber: unique symbol; | ||
declare type ExpectNumber<T> = { | ||
[error]: T; | ||
result: StrictEqualUsingTSInternalIdenticalToOperator<T, number>; | ||
[expectNumber]: T; | ||
result: ExtendsExcludingAnyOrNever<T, number>; | ||
}; | ||
declare const expectString: unique symbol; | ||
declare type ExpectString<T> = { | ||
[error]: T; | ||
result: StrictEqualUsingTSInternalIdenticalToOperator<T, string>; | ||
[expectString]: T; | ||
result: ExtendsExcludingAnyOrNever<T, string>; | ||
}; | ||
declare const expectBoolean: unique symbol; | ||
declare type ExpectBoolean<T> = { | ||
[error]: T; | ||
result: StrictEqualUsingTSInternalIdenticalToOperator<T, boolean>; | ||
[expectBoolean]: T; | ||
result: ExtendsExcludingAnyOrNever<T, boolean>; | ||
}; | ||
declare const expectVoid: unique symbol; | ||
declare type ExpectVoid<T> = { | ||
[error]: T; | ||
result: StrictEqualUsingTSInternalIdenticalToOperator<T, void>; | ||
[expectVoid]: T; | ||
result: ExtendsExcludingAnyOrNever<T, void>; | ||
}; | ||
declare const expectFunction: unique symbol; | ||
declare type ExpectFunction<T> = { | ||
[error]: T; | ||
result: Extends<T, (...args: any[]) => any>; | ||
[expectFunction]: T; | ||
result: ExtendsExcludingAnyOrNever<T, (...args: any[]) => any>; | ||
}; | ||
declare const expectObject: unique symbol; | ||
declare type ExpectObject<T> = { | ||
[error]: T; | ||
result: Extends<T, object>; | ||
[expectObject]: T; | ||
result: ExtendsExcludingAnyOrNever<T, object>; | ||
}; | ||
declare const expectArray: unique symbol; | ||
declare type ExpectArray<T> = { | ||
[error]: T; | ||
result: Extends<T, any[]>; | ||
[expectArray]: T; | ||
result: ExtendsExcludingAnyOrNever<T, any[]>; | ||
}; | ||
declare const expectSymbol: unique symbol; | ||
declare type ExpectSymbol<T> = { | ||
[error]: T; | ||
result: Extends<T, symbol>; | ||
[expectSymbol]: T; | ||
result: ExtendsExcludingAnyOrNever<T, symbol>; | ||
}; | ||
declare const expectAny: unique symbol; | ||
declare type ExpectAny<T> = { | ||
[error]: T; | ||
[expectAny]: T; | ||
result: IsAny<T>; | ||
}; | ||
declare const expectUnknown: unique symbol; | ||
declare type ExpectUnknown<T> = { | ||
[error]: T; | ||
[expectUnknown]: T; | ||
result: IsUnknown<T>; | ||
}; | ||
declare const expectNever: unique symbol; | ||
declare type ExpectNever<T> = { | ||
[error]: T; | ||
[expectNever]: T; | ||
result: IsNever<T>; | ||
}; | ||
declare const expectNullable: unique symbol; | ||
declare type ExpectNullable<T> = { | ||
[error]: T; | ||
[expectNullable]: T; | ||
result: Not<StrictEqualUsingBranding<T, NonNullable<T>>>; | ||
@@ -207,3 +221,3 @@ }; | ||
positive: boolean; | ||
}> = (Options['positive'] extends true ? PositiveExpectTypeOf<Actual> : NegativeExpectTypeOf<Actual>); | ||
}> = Options['positive'] extends true ? PositiveExpectTypeOf<Actual> : NegativeExpectTypeOf<Actual>; | ||
export interface BaseExpectTypeOf<Actual, Options extends { | ||
@@ -210,0 +224,0 @@ positive: boolean; |
@@ -5,3 +5,20 @@ "use strict"; | ||
const secret = Symbol('secret'); | ||
const error = Symbol('error'); | ||
const mismatch = Symbol('mismatch'); | ||
/** A type which should match anything passed as a value but *doesn't* match `Mismatch` - helps TypeScript select the right overload for `toEqualTypeOf` and `toMatchTypeOf`. */ | ||
const avalue = Symbol('avalue'); | ||
const inverted = Symbol('inverted'); | ||
const expectNull = Symbol('expectNull'); | ||
const expectUndefined = Symbol('expectUndefined'); | ||
const expectNumber = Symbol('expectNumber'); | ||
const expectString = Symbol('expectString'); | ||
const expectBoolean = Symbol('expectBoolean'); | ||
const expectVoid = Symbol('expectVoid'); | ||
const expectFunction = Symbol('expectFunction'); | ||
const expectObject = Symbol('expectObject'); | ||
const expectArray = Symbol('expectArray'); | ||
const expectSymbol = Symbol('expectSymbol'); | ||
const expectAny = Symbol('expectAny'); | ||
const expectUnknown = Symbol('expectUnknown'); | ||
const expectNever = Symbol('expectNever'); | ||
const expectNullable = Symbol('expectNullable'); | ||
const fn = () => true; | ||
@@ -8,0 +25,0 @@ /** |
{ | ||
"name": "expect-type", | ||
"version": "0.17.0-1", | ||
"version": "0.17.0-2", | ||
"engines": { | ||
@@ -5,0 +5,0 @@ "node": ">=12.0.0" |
@@ -36,4 +36,5 @@ # expect-type | ||
- [Where is `.toExtend`?](#where-is-toextend) | ||
- [Use internal type helpers at your own risk](#use-internal-type-helpers-at-your-own-risk) | ||
- [Internal type helpers](#internal-type-helpers) | ||
- [Error messages](#error-messages) | ||
- [Concrete "expected" objects vs typeargs](#concrete-expected-objects-vs-typeargs) | ||
- [Within test frameworks](#within-test-frameworks) | ||
@@ -69,3 +70,3 @@ - [Jest & `eslint-plugin-jest`](#jest--eslint-plugin-jest) | ||
`.toEqualTypeOf` can check that two concrete objects have equivalent types: | ||
`.toEqualTypeOf` can check that two concrete objects have equivalent types (note: when these assertions _fail_, the error messages can be less informative vs the generic typearg syntax above - see [error messages docs](#error-messages)): | ||
@@ -168,2 +169,29 @@ ```typescript | ||
`.toBe...` methods allow for types which extend the expected type: | ||
```typescript | ||
expectTypeOf<number>().toBeNumber() | ||
expectTypeOf<1>().toBeNumber() | ||
expectTypeOf<any[]>().toBeArray() | ||
expectTypeOf<number[]>().toBeArray() | ||
expectTypeOf<string>().toBeString() | ||
expectTypeOf<'foo'>().toBeString() | ||
expectTypeOf<boolean>().toBeBoolean() | ||
expectTypeOf<true>().toBeBoolean() | ||
``` | ||
`.toBe...` methods protect against `any`: | ||
```typescript | ||
const goodIntParser = (s: string) => Number.parseInt(s, 10) | ||
const badIntParser = (s: string) => JSON.parse(s) // uh-oh - works at runtime if the input is a number, but return 'any' | ||
expectTypeOf(goodIntParser).returns.toBeNumber() | ||
// @ts-expect-error - if you write a test like this, `.toBeNumber()` will let you know your implementation returns `any`. | ||
expectTypeOf(badIntParser).returns.toBeNumber() | ||
``` | ||
Nullable types: | ||
@@ -526,5 +554,5 @@ | ||
### Use internal type helpers at your own risk | ||
### Internal type helpers | ||
This library also exports some helper types for performing boolean operations on types, checking extension/equality in various ways, branding types, and checking for various special types like `never`, `any`, `unknown`. Nothing is stopping you using these beyond the following warning: | ||
🚧 This library also exports some helper types for performing boolean operations on types, checking extension/equality in various ways, branding types, and checking for various special types like `never`, `any`, `unknown`. Use at your own risk! Nothing is stopping you using these beyond this warning: | ||
@@ -546,12 +574,48 @@ >All internal types that are not documented here are _not_ part of the supported API surface, and may be renamed, modified, or removed, without warning or documentation in release notes. | ||
``` | ||
test/test.ts:9:99 - error TS2344: Type '{ a: string; }' does not satisfy the constraint '{ a: \\"Expected: string, Actual: number\\"; }'. | ||
test/test.ts:999:999 - error TS2344: Type '{ a: string; }' does not satisfy the constraint '{ a: \\"Expected: string, Actual: number\\"; }'. | ||
Types of property 'a' are incompatible. | ||
Type 'string' is not assignable to type '\\"Expected: string, Actual: number\\"'. | ||
9 expectTypeOf({a: 1}).toEqualTypeOf<{a: string}>() | ||
~~~~~~~~~~~ | ||
999 expectTypeOf({a: 1}).toEqualTypeOf<{a: string}>() | ||
``` | ||
Not that the type constraint reported is a human-readable messaging specifying both the "expected" and "actual" types. Rather than taking the sentence `Types of property 'a' are incompatible // Type 'string' is not assignable to type "Expected: string, Actual: number"` literally - just look at the property name (`'a'`) and the message: `Expected: string, Actual: number`. This will tell you what's wrong, in most cases. Extremely complex types will of course be more effort to debug, and may require some experimentation. Please [raise an issue](https://github.com/mmkal/expect-type) if the error messages are actually misleading. | ||
Note that the type constraint reported is a human-readable messaging specifying both the "expected" and "actual" types. Rather than taking the sentence `Types of property 'a' are incompatible // Type 'string' is not assignable to type "Expected: string, Actual: number"` literally - just look at the property name (`'a'`) and the message: `Expected: string, Actual: number`. This will tell you what's wrong, in most cases. Extremely complex types will of course be more effort to debug, and may require some experimentation. Please [raise an issue](https://github.com/mmkal/expect-type) if the error messages are actually misleading. | ||
The `toBe...` methods (like `toBeString`, `toBeNumber`, `toBeVoid` etc.) fail by resolving to a non-callable type when the `Actual` type under test doesn't match them. For example, the failure for an assertion like `expectTypeOf(1).toBeString()` will look something like this: | ||
``` | ||
test/test.ts:999:999 - error TS2349: This expression is not callable. | ||
Type 'ExpectString<number>' has no call signatures. | ||
999 expectTypeOf(1).toBeString() | ||
~~~~~~~~~~ | ||
``` | ||
The `This expression is not callable` part isn't all that helpful - the meaningful error is the next line, `Type 'ExpectString<number> has no call signatures`. This essentially means you passed a number but asserted it should be a string. | ||
If TypeScript added support for ["throw" types](https://github.com/microsoft/TypeScript/pull/40468) these error messagess could be improved significantly. Until then they will take a certain amount of squinting. | ||
#### Concrete "expected" objects vs typeargs | ||
Error messages for an assertion like this: | ||
```ts | ||
expectTypeOf({a: 1}).toEqualTypeOf({a: ''}) | ||
``` | ||
Will be less helpful than for an assertion like this: | ||
```ts | ||
expectTypeOf({a: 1}).toEqualTypeOf<{a: string}>() | ||
``` | ||
This is because the TypeScript compiler needs to infer the typearg for the `.toEqualTypeOf({a: ''})` style, and this library can only mark it as a failure by comparing it against a generic `Mismatch` type. So, where possible, use a typearg rather than a concrete type for `.toEqualTypeOf` and `toMatchTypeOf`. If it's much more convenient to compare two concrete types, you can use `typeof`: | ||
```ts | ||
const one = valueFromFunctionOne({some: {complex: inputs}}) | ||
const two = valueFromFunctionTwo({some: {other: inputs}}) | ||
expectTypeOf(one).toEqualTypeof<typeof two>() | ||
``` | ||
### Within test frameworks | ||
@@ -608,3 +672,3 @@ | ||
- built into existing tooling. No extra build step, cli tool, IDE extension, or lint plugin is needed. Just import the function and start writing tests. Failures will be at compile time - they'll appear in your IDE and when you run `tsc`. | ||
- small implementation with no dependencies. <200 lines of code - [take a look!](./src/index.ts) (tsd, for comparison, is [2.6MB](https://bundlephobia.com/result?p=tsd@0.13.1) because it ships a patched version of typescript). | ||
- small implementation with no dependencies. [Take a look!](./src/index.ts) (tsd, for comparison, is [2.6MB](https://bundlephobia.com/result?p=tsd@0.13.1) because it ships a patched version of typescript). | ||
@@ -611,0 +675,0 @@ ## Contributing |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
55173
375
678