mirror of
https://github.com/sindresorhus/type-fest.git
synced 2025-12-08 19:25:05 +00:00
556 lines
11 KiB
TypeScript
556 lines
11 KiB
TypeScript
import type {Exact, Opaque} from '../index';
|
|
|
|
{ // Spec - string type
|
|
type Type = string;
|
|
const function_ = <T extends Exact<Type, T>>(arguments_: T) => arguments_;
|
|
|
|
{ // It should accept string
|
|
const input = '';
|
|
function_(input);
|
|
}
|
|
|
|
{ // It should reject number
|
|
const input = 1;
|
|
// @ts-expect-error
|
|
function_(input);
|
|
}
|
|
|
|
{ // It should reject object
|
|
const input = {};
|
|
// @ts-expect-error
|
|
function_(input);
|
|
}
|
|
}
|
|
|
|
{ // Spec - bigint type
|
|
type Type = bigint;
|
|
const function_ = <T extends Exact<Type, T>>(arguments_: T) => arguments_;
|
|
|
|
{ // It should accept bigint
|
|
const input = BigInt(9_007_199_254_740_991);
|
|
function_(input);
|
|
}
|
|
|
|
{ // It should reject number
|
|
const input = 1;
|
|
// @ts-expect-error
|
|
function_(input);
|
|
}
|
|
|
|
{ // It should reject unknown
|
|
const input = {} as unknown;
|
|
// @ts-expect-error
|
|
function_(input);
|
|
}
|
|
}
|
|
|
|
{ // Spec - array
|
|
type Type = Array<{code: string; name?: string}>;
|
|
const function_ = <T extends Exact<Type, T>>(arguments_: T) => arguments_;
|
|
|
|
{ // It should accept array with required property only
|
|
const input = [{code: ''}];
|
|
function_(input);
|
|
}
|
|
|
|
{ // It should reject readonly array
|
|
const input = [{code: ''}] as ReadonlyArray<{code: string}>;
|
|
// @ts-expect-error
|
|
function_(input);
|
|
}
|
|
|
|
{ // It should accept array with optional property
|
|
const input = [{code: '', name: ''}];
|
|
function_(input);
|
|
}
|
|
|
|
{ // It should reject array with excess property
|
|
const input = [{code: '', name: '', excessProperty: ''}];
|
|
// @ts-expect-error
|
|
function_(input);
|
|
}
|
|
|
|
{ // It should reject invalid type
|
|
const input = '';
|
|
// @ts-expect-error
|
|
function_(input);
|
|
}
|
|
}
|
|
|
|
{ // Spec - readonly array
|
|
type Type = ReadonlyArray<{code: string; name?: string}>;
|
|
const function_ = <T extends Exact<Type, T>>(arguments_: T) => arguments_;
|
|
|
|
{ // It should accept array with required property only
|
|
const input = [{code: ''}];
|
|
function_(input);
|
|
}
|
|
|
|
{ // It should accept readonly array
|
|
const input = [{code: ''}] as ReadonlyArray<{code: string}>;
|
|
function_(input);
|
|
}
|
|
|
|
{ // It should accept array with optional property
|
|
const input = [{code: '', name: ''}];
|
|
function_(input);
|
|
}
|
|
|
|
{ // It should reject array with excess property
|
|
const input = [{code: '', name: '', excessProperty: ''}];
|
|
// @ts-expect-error
|
|
function_(input);
|
|
}
|
|
|
|
{ // It should reject invalid type
|
|
const input = '';
|
|
// @ts-expect-error
|
|
function_(input);
|
|
}
|
|
}
|
|
|
|
{ // Spec - object
|
|
type Type = {code: string; name?: string};
|
|
const function_ = <T extends Exact<Type, T>>(arguments_: T) => arguments_;
|
|
|
|
{ // It should accept object with required property only
|
|
const input = {code: ''};
|
|
function_(input);
|
|
}
|
|
|
|
{ // It should accept object with optional property
|
|
const input = {code: '', name: ''};
|
|
function_(input);
|
|
}
|
|
|
|
{ // It should reject object with excess property
|
|
const input = {code: '', name: '', excessProperty: ''};
|
|
// @ts-expect-error
|
|
function_(input);
|
|
}
|
|
|
|
{ // It should reject invalid type
|
|
const input = '';
|
|
// @ts-expect-error
|
|
function_(input);
|
|
}
|
|
}
|
|
|
|
{ // Spec - object with optional outer object @see https://github.com/sindresorhus/type-fest/pull/546#issuecomment-1385620838
|
|
type Type = {
|
|
outer?: {
|
|
inner: {
|
|
field: string;
|
|
};
|
|
};
|
|
};
|
|
const function_ = <T extends Exact<Type, T>>(arguments_: T) => arguments_;
|
|
function_({
|
|
outer: {
|
|
inner: {
|
|
field: 'foo',
|
|
},
|
|
},
|
|
});
|
|
}
|
|
|
|
{ // Spec - union - only object
|
|
type Type = {code: string} | {name: string};
|
|
const function_ = <T extends Exact<Type, T>>(arguments_: T) => arguments_;
|
|
|
|
{ // It should accept type a
|
|
const input = {code: ''};
|
|
function_(input);
|
|
}
|
|
|
|
{ // It should accept type b
|
|
const input = {name: ''};
|
|
function_(input);
|
|
}
|
|
|
|
{ // It should reject intersection
|
|
const input = {name: '', code: ''};
|
|
// @ts-expect-error
|
|
function_(input);
|
|
}
|
|
}
|
|
|
|
{ // Spec - union - mixture object/primitive
|
|
type Type = {code: string} | string;
|
|
const function_ = <T extends Exact<Type, T>>(arguments_: T) => arguments_;
|
|
|
|
{ // It should accept type a
|
|
const input = {code: ''};
|
|
function_(input);
|
|
}
|
|
|
|
{ // It should accept type b
|
|
const input = '';
|
|
function_(input);
|
|
}
|
|
|
|
{ // It should reject intersection
|
|
const input = {name: '', code: ''};
|
|
// @ts-expect-error
|
|
function_(input);
|
|
}
|
|
}
|
|
|
|
{ // Spec - jsonschema2ts generated request type with additionalProperties: true
|
|
type Type = {
|
|
body: {
|
|
[k: string]: unknown;
|
|
code: string;
|
|
name?: string;
|
|
};
|
|
};
|
|
const function_ = <T extends Exact<Type, T>>(arguments_: T) => arguments_;
|
|
|
|
{ // It should accept input with required property only
|
|
const input = {body: {code: ''}};
|
|
function_(input);
|
|
}
|
|
|
|
{ // It should accept input with optional property
|
|
const input = {body: {code: '', name: ''}};
|
|
function_(input);
|
|
}
|
|
|
|
{ // It should allow input with excess property
|
|
const input = {body: {code: '', name: '', excessProperty: 1}};
|
|
function_(input);
|
|
}
|
|
}
|
|
|
|
{ // Spec - check index signature type
|
|
type Type = {
|
|
body: {
|
|
[k: string]: string;
|
|
code: string;
|
|
name?: string;
|
|
};
|
|
};
|
|
const function_ = <T extends Exact<Type, T>>(arguments_: T) => arguments_;
|
|
|
|
{ // It should allow input with an excess property
|
|
const input = {body: {code: '', name: '', excessProperty: 1}};
|
|
// Expects error because the excess property is not string
|
|
// @ts-expect-error
|
|
function_(input);
|
|
}
|
|
}
|
|
|
|
{ // Spec - union of array
|
|
type Type = Array<{x: string}> & Array<{z: number}>;
|
|
const function_ = <T extends Exact<Type, T>>(arguments_: T) => arguments_;
|
|
|
|
{ // It should accept valid input
|
|
const input = [{
|
|
x: '',
|
|
z: 1,
|
|
}];
|
|
function_(input);
|
|
}
|
|
|
|
{ // It should reject missing field
|
|
const input = [{
|
|
z: 1,
|
|
}];
|
|
// @ts-expect-error
|
|
function_(input);
|
|
}
|
|
|
|
{ // It should reject missing field
|
|
const input = [{
|
|
x: '',
|
|
}];
|
|
// @ts-expect-error
|
|
function_(input);
|
|
}
|
|
|
|
{ // It should reject incorrect type
|
|
const input = [{
|
|
x: 1,
|
|
z: 1,
|
|
}];
|
|
// @ts-expect-error
|
|
function_(input);
|
|
}
|
|
|
|
{ // It should reject excess field
|
|
const input = [{
|
|
x: '',
|
|
y: '',
|
|
z: 1,
|
|
}];
|
|
// @ts-expect-error
|
|
function_(input);
|
|
}
|
|
}
|
|
|
|
{ // Spec - union of readonly array + non readonly array
|
|
type Type = ReadonlyArray<{x: string}> & Array<{z: number}>;
|
|
const function_ = <T extends Exact<Type, T>>(arguments_: T) => arguments_;
|
|
|
|
{ // It should accept valid input
|
|
const input = [{
|
|
x: '',
|
|
z: 1,
|
|
}];
|
|
function_(input);
|
|
}
|
|
|
|
{ // It should reject missing field
|
|
const input = [{
|
|
z: 1,
|
|
}];
|
|
// @ts-expect-error
|
|
function_(input);
|
|
}
|
|
|
|
{ // It should reject missing field
|
|
const input = [{
|
|
x: '',
|
|
}];
|
|
// @ts-expect-error
|
|
function_(input);
|
|
}
|
|
|
|
{ // It should reject incorrect type
|
|
const input = [{
|
|
x: 1,
|
|
z: 1,
|
|
}];
|
|
// @ts-expect-error
|
|
function_(input);
|
|
}
|
|
|
|
{ // It should reject excess field
|
|
const input = [{
|
|
x: '',
|
|
y: '',
|
|
z: 1,
|
|
}];
|
|
// @ts-expect-error
|
|
function_(input);
|
|
}
|
|
}
|
|
|
|
{ // Spec - union of array with nested fields
|
|
type Type = Array<{x: string}> & Array<{z: number; d: {e: string; f: boolean}}>;
|
|
const function_ = <T extends Exact<Type, T>>(arguments_: T) => arguments_;
|
|
|
|
{ // It should accept valid input
|
|
const input = [{
|
|
x: '',
|
|
z: 1,
|
|
d: {
|
|
e: 'test',
|
|
f: true,
|
|
},
|
|
}];
|
|
function_(input);
|
|
}
|
|
|
|
{ // It should reject excess field
|
|
const input = [{
|
|
x: '',
|
|
z: 1,
|
|
d: {
|
|
e: 'test',
|
|
f: true,
|
|
g: '', // Excess field
|
|
},
|
|
}];
|
|
// @ts-expect-error
|
|
function_(input);
|
|
}
|
|
|
|
{ // It should reject missing field
|
|
const input = [{
|
|
x: '',
|
|
z: 1,
|
|
d: {
|
|
e: 'test',
|
|
// Missing f: boolean
|
|
},
|
|
}];
|
|
// @ts-expect-error
|
|
function_(input);
|
|
}
|
|
|
|
{ // It should reject missing field
|
|
const input = [{
|
|
x: '',
|
|
z: 1,
|
|
d: {
|
|
e: 'test',
|
|
f: '', // Type mismatch
|
|
},
|
|
}];
|
|
// @ts-expect-error
|
|
function_(input);
|
|
}
|
|
}
|
|
|
|
// Spec - special test case for Opaque types
|
|
// @see https://github.com/sindresorhus/type-fest/issues/508
|
|
{
|
|
type SpecialName = Opaque<string, 'special name'>;
|
|
|
|
type OnlyAcceptName = {
|
|
name: SpecialName;
|
|
};
|
|
|
|
const onlyAcceptNameImproved = <T extends Exact<OnlyAcceptName, T>>(arguments_: T) => arguments_;
|
|
|
|
onlyAcceptNameImproved({
|
|
// The error before the workaround:
|
|
// Error: Type 'SpecialName' is not assignable to type 'never'
|
|
name: 'name' as SpecialName,
|
|
});
|
|
}
|
|
|
|
// Spec - special test case for Opaque type
|
|
// @see https://github.com/sindresorhus/type-fest/issues/508
|
|
{
|
|
// Test for number Opaque type
|
|
type SpecialName = Opaque<number, 'special name'>;
|
|
|
|
type OnlyAcceptName = {
|
|
name: SpecialName;
|
|
};
|
|
|
|
const function_ = <T extends Exact<OnlyAcceptName, T>>(arguments_: T) => arguments_;
|
|
|
|
function_({
|
|
// The error before the workaround:
|
|
// Error: Type 'SpecialName' is not assignable to type 'never'
|
|
name: 1 as SpecialName,
|
|
});
|
|
}
|
|
|
|
// Spec - test the above for tagged types too.
|
|
{
|
|
type TaggedNumber = Opaque<number, 'tag'>;
|
|
|
|
const function_ = <T extends Exact<{a: TaggedNumber}, T>>(arguments_: T) => arguments_;
|
|
|
|
function_({a: 1 as TaggedNumber});
|
|
// @ts-expect-error
|
|
function_({a: 1 as TaggedNumber, b: true});
|
|
}
|
|
|
|
// Spec - special test case for deep optional union
|
|
// https://github.com/sindresorhus/type-fest/issues/545
|
|
{
|
|
type ParameterType = {
|
|
outer?: {
|
|
inner?: {
|
|
union: 'foo' | 'bar';
|
|
};
|
|
};
|
|
};
|
|
|
|
const function_ = <InputT extends Exact<ParameterType, InputT>>(arguments_: InputT) => arguments_;
|
|
|
|
// Test input with declared type
|
|
type Input = {
|
|
outer?: {
|
|
inner?: {
|
|
union: 'foo' | 'bar';
|
|
};
|
|
};
|
|
};
|
|
const variableWithDeclaredType: Input = {
|
|
outer: {
|
|
inner: {
|
|
union: 'foo',
|
|
},
|
|
},
|
|
};
|
|
function_(variableWithDeclaredType);
|
|
|
|
// Test input without declared type
|
|
const variableWithoutDeclaredType = {
|
|
outer: {
|
|
inner: {
|
|
union: 'foo' as const,
|
|
},
|
|
},
|
|
};
|
|
function_(variableWithoutDeclaredType);
|
|
|
|
// Test input with plain object
|
|
function_({
|
|
outer: {
|
|
inner: {
|
|
union: 'foo',
|
|
},
|
|
},
|
|
});
|
|
}
|
|
|
|
// Spec - special test case for Date type + union
|
|
// @see https://github.com/sindresorhus/type-fest/issues/896
|
|
{
|
|
type A = {
|
|
a: string;
|
|
b?: Date | null;
|
|
};
|
|
|
|
const function_ = <T extends Exact<A, T>>(arguments_: T) => arguments_;
|
|
|
|
function_({a: 'a'});
|
|
function_({a: 'a', b: new Date()});
|
|
function_({a: 'a', b: new Date() as Date | null});
|
|
// @ts-expect-error
|
|
function_({a: 'a', b: 1});
|
|
}
|
|
|
|
// Spec - special test case for Date type
|
|
// @see https://github.com/sindresorhus/type-fest/issues/909
|
|
{
|
|
type UserType = {
|
|
id: string;
|
|
name: string;
|
|
createdAt: Date;
|
|
email?: string;
|
|
};
|
|
|
|
const function_ = <T extends Exact<UserType, T>>(arguments_: T) => arguments_;
|
|
|
|
function_({
|
|
id: 'asd',
|
|
name: 'John',
|
|
createdAt: new Date(),
|
|
});
|
|
|
|
const withExcessSurname = {
|
|
id: 'asd',
|
|
name: 'John',
|
|
createdAt: new Date(),
|
|
surname: 'Doe',
|
|
};
|
|
|
|
// Expects error due to surname is an excess field
|
|
// @ts-expect-error
|
|
function_(withExcessSurname);
|
|
}
|
|
|
|
// Spec - recursive type with union
|
|
// @see https://github.com/sindresorhus/type-fest/issues/948
|
|
{
|
|
type A = {
|
|
a: Expected;
|
|
};
|
|
type B = {
|
|
b: string;
|
|
};
|
|
type Expected = A | B;
|
|
|
|
const function_ = <T extends Exact<Expected, T>>(arguments_: T) => arguments_;
|
|
|
|
function_({} as Expected);
|
|
}
|