mirror of
https://github.com/sindresorhus/type-fest.git
synced 2025-12-08 19:25:05 +00:00
442 lines
8.1 KiB
TypeScript
442 lines
8.1 KiB
TypeScript
import type {Exact, Opaque} from '../index';
|
|
|
|
{ // Spec - string type
|
|
type Type = string;
|
|
const fn = <T extends Exact<Type, T>>(arguments_: T) => arguments_;
|
|
|
|
{ // It should accept string
|
|
const input = '';
|
|
fn(input);
|
|
}
|
|
|
|
{ // It should reject number
|
|
const input = 1;
|
|
// @ts-expect-error
|
|
fn(input);
|
|
}
|
|
|
|
{ // It should reject object
|
|
const input = {};
|
|
// @ts-expect-error
|
|
fn(input);
|
|
}
|
|
}
|
|
|
|
{ // Spec - array
|
|
type Type = Array<{code: string; name?: string}>;
|
|
const fn = <T extends Exact<Type, T>>(arguments_: T) => arguments_;
|
|
|
|
{ // It should accept array with required property only
|
|
const input = [{code: ''}];
|
|
fn(input);
|
|
}
|
|
|
|
{ // It should reject readonly array
|
|
const input = [{code: ''}] as ReadonlyArray<{code: string}>;
|
|
// @ts-expect-error
|
|
fn(input);
|
|
}
|
|
|
|
{ // It should accept array with optional property
|
|
const input = [{code: '', name: ''}];
|
|
fn(input);
|
|
}
|
|
|
|
{ // It should reject array with excess property
|
|
const input = [{code: '', name: '', excessProperty: ''}];
|
|
// @ts-expect-error
|
|
fn(input);
|
|
}
|
|
|
|
{ // It should reject invalid type
|
|
const input = '';
|
|
// @ts-expect-error
|
|
fn(input);
|
|
}
|
|
}
|
|
|
|
{ // Spec - readonly array
|
|
type Type = ReadonlyArray<{code: string; name?: string}>;
|
|
const fn = <T extends Exact<Type, T>>(arguments_: T) => arguments_;
|
|
|
|
{ // It should accept array with required property only
|
|
const input = [{code: ''}];
|
|
fn(input);
|
|
}
|
|
|
|
{ // It should accept readonly array
|
|
const input = [{code: ''}] as ReadonlyArray<{code: string}>;
|
|
fn(input);
|
|
}
|
|
|
|
{ // It should accept array with optional property
|
|
const input = [{code: '', name: ''}];
|
|
fn(input);
|
|
}
|
|
|
|
{ // It should reject array with excess property
|
|
const input = [{code: '', name: '', excessProperty: ''}];
|
|
// @ts-expect-error
|
|
fn(input);
|
|
}
|
|
|
|
{ // It should reject invalid type
|
|
const input = '';
|
|
// @ts-expect-error
|
|
fn(input);
|
|
}
|
|
}
|
|
|
|
{ // Spec - object
|
|
type Type = {code: string; name?: string};
|
|
const fn = <T extends Exact<Type, T>>(arguments_: T) => arguments_;
|
|
|
|
{ // It should accept object with required property only
|
|
const input = {code: ''};
|
|
fn(input);
|
|
}
|
|
|
|
{ // It should accept object with optional property
|
|
const input = {code: '', name: ''};
|
|
fn(input);
|
|
}
|
|
|
|
{ // It should reject object with excess property
|
|
const input = {code: '', name: '', excessProperty: ''};
|
|
// @ts-expect-error
|
|
fn(input);
|
|
}
|
|
|
|
{ // It should reject invalid type
|
|
const input = '';
|
|
// @ts-expect-error
|
|
fn(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 fn = <T extends Exact<Type, T>>(arguments_: T) => arguments_;
|
|
fn({
|
|
outer: {
|
|
inner: {
|
|
field: 'foo',
|
|
},
|
|
},
|
|
});
|
|
}
|
|
|
|
{ // Spec - union - only object
|
|
type Type = {code: string} | {name: string};
|
|
const fn = <T extends Exact<Type, T>>(arguments_: T) => arguments_;
|
|
|
|
{ // It should accept type a
|
|
const input = {code: ''};
|
|
fn(input);
|
|
}
|
|
|
|
{ // It should accept type b
|
|
const input = {name: ''};
|
|
fn(input);
|
|
}
|
|
|
|
{ // It should reject intersection
|
|
const input = {name: '', code: ''};
|
|
// @ts-expect-error
|
|
fn(input);
|
|
}
|
|
}
|
|
|
|
{ // Spec - union - mixture object/primitive
|
|
type Type = {code: string} | string;
|
|
const fn = <T extends Exact<Type, T>>(arguments_: T) => arguments_;
|
|
|
|
{ // It should accept type a
|
|
const input = {code: ''};
|
|
fn(input);
|
|
}
|
|
|
|
{ // It should accept type b
|
|
const input = '';
|
|
fn(input);
|
|
}
|
|
|
|
{ // It should reject intersection
|
|
const input = {name: '', code: ''};
|
|
// @ts-expect-error
|
|
fn(input);
|
|
}
|
|
}
|
|
|
|
{ // Spec - jsonschema2ts generated request type with additionalProperties: true
|
|
type Type = {
|
|
body: {
|
|
[k: string]: unknown;
|
|
code: string;
|
|
name?: string;
|
|
};
|
|
};
|
|
const fn = <T extends Exact<Type, T>>(arguments_: T) => arguments_;
|
|
|
|
{ // It should accept input with required property only
|
|
const input = {body: {code: ''}};
|
|
fn(input);
|
|
}
|
|
|
|
{ // It should accept input with optional property
|
|
const input = {body: {code: '', name: ''}};
|
|
fn(input);
|
|
}
|
|
|
|
{ // It should allow input with excess property
|
|
const input = {body: {code: '', name: '', excessProperty: ''}};
|
|
fn(input);
|
|
}
|
|
}
|
|
|
|
{ // Spec - union of array
|
|
type Type = Array<{x: string}> & Array<{z: number}>;
|
|
const fn = <T extends Exact<Type, T>>(arguments_: T) => arguments_;
|
|
|
|
{ // It should accept valid input
|
|
const input = [{
|
|
x: '',
|
|
z: 1,
|
|
}];
|
|
fn(input);
|
|
}
|
|
|
|
{ // It should reject missing field
|
|
const input = [{
|
|
z: 1,
|
|
}];
|
|
// @ts-expect-error
|
|
fn(input);
|
|
}
|
|
|
|
{ // It should reject missing field
|
|
const input = [{
|
|
x: '',
|
|
}];
|
|
// @ts-expect-error
|
|
fn(input);
|
|
}
|
|
|
|
{ // It should reject incorrect type
|
|
const input = [{
|
|
x: 1,
|
|
z: 1,
|
|
}];
|
|
// @ts-expect-error
|
|
fn(input);
|
|
}
|
|
|
|
{ // It should reject excess field
|
|
const input = [{
|
|
x: '',
|
|
y: '',
|
|
z: 1,
|
|
}];
|
|
// @ts-expect-error
|
|
fn(input);
|
|
}
|
|
}
|
|
|
|
{ // Spec - union of readonly array + non readonly array
|
|
type Type = ReadonlyArray<{x: string}> & Array<{z: number}>;
|
|
const fn = <T extends Exact<Type, T>>(arguments_: T) => arguments_;
|
|
|
|
{ // It should accept valid input
|
|
const input = [{
|
|
x: '',
|
|
z: 1,
|
|
}];
|
|
fn(input);
|
|
}
|
|
|
|
{ // It should reject missing field
|
|
const input = [{
|
|
z: 1,
|
|
}];
|
|
// @ts-expect-error
|
|
fn(input);
|
|
}
|
|
|
|
{ // It should reject missing field
|
|
const input = [{
|
|
x: '',
|
|
}];
|
|
// @ts-expect-error
|
|
fn(input);
|
|
}
|
|
|
|
{ // It should reject incorrect type
|
|
const input = [{
|
|
x: 1,
|
|
z: 1,
|
|
}];
|
|
// @ts-expect-error
|
|
fn(input);
|
|
}
|
|
|
|
{ // It should reject excess field
|
|
const input = [{
|
|
x: '',
|
|
y: '',
|
|
z: 1,
|
|
}];
|
|
// @ts-expect-error
|
|
fn(input);
|
|
}
|
|
}
|
|
|
|
{ // Spec - union of array with nested fields
|
|
type Type = Array<{x: string}> & Array<{z: number; d: {e: string; f: boolean}}>;
|
|
const fn = <T extends Exact<Type, T>>(arguments_: T) => arguments_;
|
|
|
|
{ // It should accept valid input
|
|
const input = [{
|
|
x: '',
|
|
z: 1,
|
|
d: {
|
|
e: 'test',
|
|
f: true,
|
|
},
|
|
}];
|
|
fn(input);
|
|
}
|
|
|
|
{ // It should reject excess field
|
|
const input = [{
|
|
x: '',
|
|
z: 1,
|
|
d: {
|
|
e: 'test',
|
|
f: true,
|
|
g: '', // Excess field
|
|
},
|
|
}];
|
|
// @ts-expect-error
|
|
fn(input);
|
|
}
|
|
|
|
{ // It should reject missing field
|
|
const input = [{
|
|
x: '',
|
|
z: 1,
|
|
d: {
|
|
e: 'test',
|
|
// Missing f: boolean
|
|
},
|
|
}];
|
|
// @ts-expect-error
|
|
fn(input);
|
|
}
|
|
|
|
{ // It should reject missing field
|
|
const input = [{
|
|
x: '',
|
|
z: 1,
|
|
d: {
|
|
e: 'test',
|
|
f: '', // Type mismatch
|
|
},
|
|
}];
|
|
// @ts-expect-error
|
|
fn(input);
|
|
}
|
|
}
|
|
|
|
// Spec - special test case for Opaque type
|
|
// @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 fn = <T extends Exact<OnlyAcceptName, T>>(arguments_: T) => arguments_;
|
|
|
|
fn({
|
|
// The error before the workaround:
|
|
// Error: Type 'SpecialName' is not assignable to type 'never'
|
|
name: 1 as SpecialName,
|
|
});
|
|
}
|
|
|
|
// Spec - special test case for deep optional union
|
|
// https://github.com/sindresorhus/type-fest/issues/545
|
|
{
|
|
type ParameterType = {
|
|
outer?: {
|
|
inner?: {
|
|
union: 'foo' | 'bar';
|
|
};
|
|
};
|
|
};
|
|
|
|
const fn = <InputT extends Exact<ParameterType, InputT>>(arguments_: InputT) => arguments_;
|
|
|
|
// Test input with declared type
|
|
type Input = {
|
|
outer?: {
|
|
inner?: {
|
|
union: 'foo' | 'bar';
|
|
};
|
|
};
|
|
};
|
|
const varWithDeclaredType: Input = {
|
|
outer: {
|
|
inner: {
|
|
union: 'foo',
|
|
},
|
|
},
|
|
};
|
|
fn(varWithDeclaredType);
|
|
|
|
// Test input without declared type
|
|
const varWithoutDeclaredType = {
|
|
outer: {
|
|
inner: {
|
|
union: 'foo' as const,
|
|
},
|
|
},
|
|
};
|
|
fn(varWithoutDeclaredType);
|
|
|
|
// Test input with plain object
|
|
fn({
|
|
outer: {
|
|
inner: {
|
|
union: 'foo',
|
|
},
|
|
},
|
|
});
|
|
}
|