Merge pull request #17905 from strapi/types/utils-tests

Start Typescript Tests
This commit is contained in:
Christian 2023-09-21 17:57:31 +02:00 committed by GitHub
commit d62456ea47
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 981 additions and 48 deletions

View File

@ -166,6 +166,7 @@
"@types/statuses": "2.0.1",
"eslint-config-custom": "4.13.7",
"supertest": "6.3.3",
"ts-zen": "https://github.com/strapi/ts-zen#66e02232f5997674cc7032ea3ee59d9864863732",
"tsconfig": "4.13.7",
"typescript": "5.2.2"
},

View File

@ -53,7 +53,8 @@
"https-proxy-agent": "5.0.1",
"koa": "2.13.4",
"node-fetch": "2.7.0",
"node-schedule": "2.1.0"
"node-schedule": "2.1.0",
"ts-zen": "https://github.com/strapi/ts-zen#66e02232f5997674cc7032ea3ee59d9864863732"
},
"devDependencies": {
"@types/jest": "29.5.2",
@ -61,7 +62,6 @@
"@types/koa__router": "12.0.0",
"@types/node-schedule": "2.1.0",
"eslint-config-custom": "4.13.7",
"ts-zen": "https://github.com/strapi/ts-zen#66e02232f5997674cc7032ea3ee59d9864863732",
"tsconfig": "4.13.7",
"typescript": "5.2.2"
},

View File

@ -0,0 +1,46 @@
import { Utils } from '../../..';
type Obj = {
foo: 'bar';
baz: false;
prop: {
foo: 'bar';
bar: 'foo';
};
};
type StringValues = Utils.Array.Values<['foo', 'bar', 'baz']>;
type NumberValues = Utils.Array.Values<[1, 2, 3]>;
type BoolValues = Utils.Array.Values<[true, false, true]>;
type TrueBoolLiteralValues = Utils.Array.Values<[true, true, true]>;
type FalseBoolLiteralValues = Utils.Array.Values<[false, false, false]>;
type ObjectValues = Utils.Array.Values<[Obj, { prop1: true; prop2: false }]>;
type MixedValues = Utils.Array.Values<[Obj, 1, 'foo', true]>;
type ContainsNever = Utils.Array.Values<[never, Obj, 1, 'foo', true]>;
// TODO move this to tuple utils
// Is Empty
type IsEmptyWithEmptyTuple = Utils.Array.IsEmpty<[]>;
type IsEmptyWithNotEmptyTuple = Utils.Array.IsEmpty<['foo', 'bar']>;
// Is Not Empty
type IsNotEmptyWithNotEmptyTuple = Utils.Array.IsNotEmpty<['foo', 'bar']>;
type IsNotEmptyWithEmptyTuple = Utils.Array.IsNotEmpty<[]>;
export {
StringValues,
NumberValues,
BoolValues,
TrueBoolLiteralValues,
FalseBoolLiteralValues,
ObjectValues,
MixedValues,
ContainsNever,
IsEmptyWithEmptyTuple,
IsEmptyWithNotEmptyTuple,
IsNotEmptyWithNotEmptyTuple,
IsNotEmptyWithEmptyTuple,
};

View File

@ -0,0 +1,341 @@
import { Utils } from '../../..';
// IsNever
type IsNeverGivenNever = Utils.Expression.IsNever<never>;
type IsNeverNotGivenNever = Utils.Expression.IsNever<string>;
type IsNeverGivenUnknown = Utils.Expression.IsNever<unknown>;
// IsNotNever
type IsNotNeverGivenNever = Utils.Expression.IsNotNever<never>;
type IsNotNeverGivenUnknown = Utils.Expression.IsNotNever<unknown>;
type IsNotNeverGivenString = Utils.Expression.IsNotNever<string>;
type IsNotNeverGivenStringLiteral = Utils.Expression.IsNotNever<'test'>;
// IsTrue
type IsTrueGivenTrue = Utils.Expression.IsTrue<true>;
type IsTrueGivenFalse = Utils.Expression.IsTrue<false>;
type IsTrueGivenBoolean = Utils.Expression.IsTrue<boolean>;
type IsTrueGivenNonBoolean = Utils.Expression.IsTrue<number>;
type IsTrueGivenNonBooleanLiteral = Utils.Expression.IsTrue<10>;
type IsTrueGivenOne = Utils.Expression.IsTrue<1>;
type IsTrueGivenZero = Utils.Expression.IsTrue<0>;
// IsFalse
type IsFalseGivenTrue = Utils.Expression.IsFalse<true>;
type IsFalseGivenFalse = Utils.Expression.IsFalse<false>;
type IsFalseGivenBoolean = Utils.Expression.IsFalse<boolean>;
type IsFalseGivenNonBoolean = Utils.Expression.IsFalse<number>;
type IsFalseGivenNonBooleanLiteral = Utils.Expression.IsFalse<10>;
type IsFalseGivenOne = Utils.Expression.IsFalse<1>;
type IsFalseGivenZero = Utils.Expression.IsFalse<0>;
// Strict Equal
type IsStrictEqualGivenNever = Utils.Expression.StrictEqual<never, never>;
type IsStrictEqualGivenNeverAndUnknown = Utils.Expression.StrictEqual<never, unknown>;
type IsStrictEqualGivenEqualLiterals = Utils.Expression.StrictEqual<1, 1>;
type IsStrictEqualGivenEqualTypes = Utils.Expression.StrictEqual<boolean, boolean>;
type IsStrictEqualGivenDifferentLiterals = Utils.Expression.StrictEqual<1, 2>;
type IsStrictEqualGivenDifferentTypes = Utils.Expression.StrictEqual<boolean, string>;
type IsStrictEqualGivenStringAndStringLiteral = Utils.Expression.StrictEqual<string, 'hello'>;
type IsStrictEqualGivenStringLiteralAndString = Utils.Expression.StrictEqual<'hello', string>;
type IsStrictEqualGivenNumberAndNumberLiteral = Utils.Expression.StrictEqual<number, 1>;
type IsStrictEqualGivenNumberLiteralAndNumber = Utils.Expression.StrictEqual<1, number>;
// Extends
type StringExtendsString = Utils.Expression.Extends<string, string>;
type StringLiteralExtendsString = Utils.Expression.Extends<'text', string>;
type StringExtendsStringLiteral = Utils.Expression.Extends<string, 'text'>;
type StringExtendsNumber = Utils.Expression.Extends<string, number>;
type StringLiteralExtendsNumber = Utils.Expression.Extends<'text', number>;
type StringExtendsNumberLiteral = Utils.Expression.Extends<string, 10>;
type NumberExtendsNumber = Utils.Expression.Extends<number, number>;
type NumberLiteralExtendsNumber = Utils.Expression.Extends<10, number>;
type NumberExtendsNumberLiteral = Utils.Expression.Extends<number, 10>;
type NumberExtendsString = Utils.Expression.Extends<number, string>;
type NumberLiteralExtendsString = Utils.Expression.Extends<10, string>;
type NumberExtendsStringLiteral = Utils.Expression.Extends<number, 'text'>;
type ObjectExtendsObject = Utils.Expression.Extends<object, object>;
type ObjectLiteralExtendsObject = Utils.Expression.Extends<{ test: 1 }, object>;
type ObjectExtendsObjectLiteral = Utils.Expression.Extends<object, { test: 1 }>;
type ObjectExtendsNumber = Utils.Expression.Extends<object, number>;
type ObjectExtendsAny = Utils.Expression.Extends<object, any>;
type ObjectExtendsUnknown = Utils.Expression.Extends<object, unknown>;
type ObjectExtendsNever = Utils.Expression.Extends<object, never>;
type ArrayExtendsArray = Utils.Expression.Extends<Array<string>, Array<string>>;
type TupleExtendsArray = Utils.Expression.Extends<[string], Array<string>>;
type StringArrayExtendsArray = Utils.Expression.Extends<string[], Array<string>>;
// NotExtends
type StringNotExtendsString = Utils.Expression.DoesNotExtends<string, string>;
type StringLiteralNotExtendsString = Utils.Expression.DoesNotExtends<'text', string>;
type StringNotExtendsStringLiteral = Utils.Expression.DoesNotExtends<string, 'text'>;
type StringNotExtendsNumber = Utils.Expression.DoesNotExtends<string, number>;
type StringLiteralNotExtendsNumber = Utils.Expression.DoesNotExtends<'text', number>;
type StringNotExtendsNumberLiteral = Utils.Expression.DoesNotExtends<string, 10>;
type NumberNotExtendsNumber = Utils.Expression.DoesNotExtends<number, number>;
type NumberLiteralNotExtendsNumber = Utils.Expression.DoesNotExtends<10, number>;
type NumberNotExtendsNumberLiteral = Utils.Expression.DoesNotExtends<number, 10>;
type NumberNotExtendsString = Utils.Expression.DoesNotExtends<number, string>;
type NumberLiteralNotExtendsString = Utils.Expression.DoesNotExtends<10, string>;
type NumberNotExtendsStringLiteral = Utils.Expression.DoesNotExtends<number, 'text'>;
type ObjectNotExtendsObject = Utils.Expression.DoesNotExtends<object, object>;
type ObjectLiteralNotExtendsObject = Utils.Expression.DoesNotExtends<{ test: 1 }, object>;
type ObjectNotExtendsObjectLiteral = Utils.Expression.DoesNotExtends<object, { test: 1 }>;
type ObjectNotExtendsNumber = Utils.Expression.DoesNotExtends<object, number>;
type ObjectNotExtendsAny = Utils.Expression.DoesNotExtends<object, any>;
type ObjectNotExtendsUnknown = Utils.Expression.DoesNotExtends<object, unknown>;
type ObjectNotExtendsNever = Utils.Expression.DoesNotExtends<object, never>;
type ArrayNotExtendsArray = Utils.Expression.DoesNotExtends<Array<string>, Array<string>>;
type TupleNotExtendsArray = Utils.Expression.DoesNotExtends<[string], Array<string>>;
type StringArrayNotExtendsArray = Utils.Expression.DoesNotExtends<string[], Array<string>>;
// If
type IfTrue = Utils.Expression.If<true, true, false>;
type IfFalse = Utils.Expression.If<false, true, false>;
type IfBoolean = Utils.Expression.If<boolean, true, false>;
type IfNumber = Utils.Expression.If<number, true, false>;
type IfString = Utils.Expression.If<string, true, false>;
type IfObject = Utils.Expression.If<object, true, false>;
type IfUnknown = Utils.Expression.If<unknown, true, false>;
type IfAny = Utils.Expression.If<any, true, false>;
type IfNever = Utils.Expression.If<never, true, false>;
type IfStringLiteral = Utils.Expression.If<'test', true, false>;
type IfNumberLiteral = Utils.Expression.If<10, true, false>;
type IfObjectLiteral = Utils.Expression.If<{ test: 1 }, true, false>;
type IfTuple = Utils.Expression.If<[1, 2, 3], true, false>;
type IfArray = Utils.Expression.If<Array<string>, true, false>;
type IfStringArray = Utils.Expression.If<string[], true, false>;
type IfTupleArray = Utils.Expression.If<[string, number], true, false>;
type IfUnion = Utils.Expression.If<string | number, true, false>;
type IfIntersection = Utils.Expression.If<string & number, true, false>;
type IfFunction = Utils.Expression.If<() => void, true, false>;
type IfClass = Utils.Expression.If<new () => void, true, false>;
type IfVoid = Utils.Expression.If<void, true, false>;
type IfNull = Utils.Expression.If<null, true, false>;
type IfUndefined = Utils.Expression.If<undefined, true, false>;
type IfWithStringReturnType = Utils.Expression.If<true, 'test', 'test2'>;
type IfWithNumberReturnType = Utils.Expression.If<true, 1, 2>;
type IfWithBooleanReturnType = Utils.Expression.If<true, true, false>;
type IfWithObjectReturnType = Utils.Expression.If<true, { foo: 1 }, { bar: 'bar' }>;
type IfWithTupleReturnType = Utils.Expression.If<true, [1, 2, 3], [4, 5, 6]>;
// TODO Check this type
type IfWithArrayReturnType = Utils.Expression.If<true, Array<string>, Array<number>>;
type IfWithUnionReturnType = Utils.Expression.If<true, string | number, string & number>;
type IfWithVoidReturnType = Utils.Expression.If<true, void, number>;
type IfWithNullReturnType = Utils.Expression.If<true, null, number>;
type IfWithUndefinedReturnType = Utils.Expression.If<true, undefined, number>;
type IfWithNeverReturnType = Utils.Expression.If<true, never, number>;
type IfWithUnknownReturnType = Utils.Expression.If<true, unknown, number>;
type IfWithAnyReturnType = Utils.Expression.If<true, any, number>;
// MatchFirst
type MatchFirstReturnsTestSuccessful = Utils.Expression.MatchFirst<
[[Utils.Expression.IsTrue<Utils.Expression.True>, 'test-successful']],
'fail'
>;
type MatchFirstReturnsDefault = Utils.Expression.MatchFirst<
[[Utils.Expression.IsTrue<Utils.Expression.False>, 'test-successful']],
'default'
>;
type MatchFirstReturnsNeverWithEmptyTests = Utils.Expression.MatchFirst<[], 'default'>;
type MatchFirstReturnsNeverWithNeverDefault = Utils.Expression.MatchFirst<
[[Utils.Expression.IsTrue<Utils.Expression.False>, 'test-successful']],
never
>;
// MatchAllIntersect
type MatchAllIntersectWithOneTrueCondition = Utils.Expression.MatchAllIntersect<
[
[Utils.Expression.IsTrue<Utils.Expression.True>, { test: 1 }],
[Utils.Expression.IsTrue<Utils.Expression.False>, { test: 2 }]
]
>;
type MatchAllIntersectWithAllFalseConditions = Utils.Expression.MatchAllIntersect<
[
[Utils.Expression.IsTrue<Utils.Expression.False>, { test: 1 }],
[Utils.Expression.IsTrue<Utils.Expression.False>, { test: 2 }]
]
>;
type MatchAllIntersectWithIntersection = Utils.Expression.MatchAllIntersect<
[
[Utils.Expression.Extends<'test', string>, 'test'],
[Utils.Expression.Extends<'test', string>, 'test']
]
>;
// Test
type TestPasses = Utils.Expression.Test<Utils.Expression.IsTrue<Utils.Expression.True>, 'test'>;
type TestFails = Utils.Expression.Test<Utils.Expression.IsTrue<Utils.Expression.False>, 'test'>;
// And
type AndTrue = Utils.Expression.And<
Utils.Expression.IsTrue<Utils.Expression.True>,
Utils.Expression.IsTrue<Utils.Expression.True>
>;
type AndFalse = Utils.Expression.And<
Utils.Expression.IsTrue<Utils.Expression.True>,
Utils.Expression.IsTrue<Utils.Expression.False>
>;
// Or
type OrTrue = Utils.Expression.Or<
Utils.Expression.IsTrue<Utils.Expression.True>,
Utils.Expression.IsTrue<Utils.Expression.True>
>;
type OrFalse = Utils.Expression.Or<
Utils.Expression.IsTrue<Utils.Expression.False>,
Utils.Expression.IsTrue<Utils.Expression.False>
>;
type OrTrueFalse = Utils.Expression.Or<
Utils.Expression.IsTrue<Utils.Expression.True>,
Utils.Expression.IsTrue<Utils.Expression.False>
>;
export {
// IsNever
IsNeverGivenNever,
IsNeverNotGivenNever,
IsNeverGivenUnknown,
// IsNotNever
IsNotNeverGivenNever,
IsNotNeverGivenUnknown,
IsNotNeverGivenString,
IsNotNeverGivenStringLiteral,
// Is Strict Equal
IsStrictEqualGivenNever,
IsStrictEqualGivenNeverAndUnknown,
IsStrictEqualGivenEqualLiterals,
IsStrictEqualGivenEqualTypes,
IsStrictEqualGivenDifferentLiterals,
IsStrictEqualGivenDifferentTypes,
IsStrictEqualGivenStringLiteralAndString,
IsStrictEqualGivenStringAndStringLiteral,
IsStrictEqualGivenNumberAndNumberLiteral,
IsStrictEqualGivenNumberLiteralAndNumber,
// IsTrue
IsTrueGivenTrue,
IsTrueGivenFalse,
IsTrueGivenBoolean,
IsTrueGivenNonBoolean,
IsTrueGivenNonBooleanLiteral,
IsTrueGivenOne,
IsTrueGivenZero,
// IsFalse
IsFalseGivenTrue,
IsFalseGivenFalse,
IsFalseGivenBoolean,
IsFalseGivenNonBoolean,
IsFalseGivenNonBooleanLiteral,
IsFalseGivenOne,
IsFalseGivenZero,
// Extends
StringExtendsString,
StringLiteralExtendsString,
StringExtendsStringLiteral,
StringExtendsNumber,
StringLiteralExtendsNumber,
StringExtendsNumberLiteral,
NumberExtendsNumber,
NumberLiteralExtendsNumber,
NumberExtendsNumberLiteral,
NumberExtendsString,
NumberLiteralExtendsString,
NumberExtendsStringLiteral,
ObjectExtendsObject,
ObjectLiteralExtendsObject,
ObjectExtendsObjectLiteral,
ObjectExtendsNumber,
ObjectExtendsAny,
ObjectExtendsUnknown,
ObjectExtendsNever,
ArrayExtendsArray,
TupleExtendsArray,
StringArrayExtendsArray,
// NotExtends
StringNotExtendsString,
StringLiteralNotExtendsString,
StringNotExtendsStringLiteral,
StringNotExtendsNumber,
StringLiteralNotExtendsNumber,
StringNotExtendsNumberLiteral,
NumberNotExtendsNumber,
NumberLiteralNotExtendsNumber,
NumberNotExtendsNumberLiteral,
NumberNotExtendsString,
NumberLiteralNotExtendsString,
NumberNotExtendsStringLiteral,
ObjectNotExtendsObject,
ObjectLiteralNotExtendsObject,
ObjectNotExtendsObjectLiteral,
ObjectNotExtendsNumber,
ObjectNotExtendsAny,
ObjectNotExtendsUnknown,
ObjectNotExtendsNever,
ArrayNotExtendsArray,
TupleNotExtendsArray,
StringArrayNotExtendsArray,
// If
IfTrue,
IfFalse,
IfBoolean,
IfNumber,
IfString,
IfObject,
IfUnknown,
IfAny,
IfNever,
IfStringLiteral,
IfNumberLiteral,
IfObjectLiteral,
IfTuple,
IfArray,
IfStringArray,
IfTupleArray,
IfUnion,
IfIntersection,
IfFunction,
IfClass,
IfVoid,
IfNull,
IfUndefined,
IfWithStringReturnType,
IfWithNumberReturnType,
IfWithBooleanReturnType,
IfWithObjectReturnType,
IfWithTupleReturnType,
IfWithArrayReturnType,
IfWithUnionReturnType,
IfWithVoidReturnType,
IfWithNullReturnType,
IfWithUndefinedReturnType,
IfWithNeverReturnType,
IfWithUnknownReturnType,
IfWithAnyReturnType,
// MAtchFirst
MatchFirstReturnsTestSuccessful,
MatchFirstReturnsDefault,
MatchFirstReturnsNeverWithEmptyTests,
MatchFirstReturnsNeverWithNeverDefault,
// MatchAllIntersect
MatchAllIntersectWithOneTrueCondition,
MatchAllIntersectWithAllFalseConditions,
MatchAllIntersectWithIntersection,
// Test
TestPasses,
TestFails,
// And
AndTrue,
AndFalse,
// Or
OrTrue,
OrFalse,
OrTrueFalse,
};

View File

@ -0,0 +1,64 @@
import { Utils } from '../../..';
// Never Guard
type NeverGuardGetsNeverWithDefaultFallback = Utils.Guard.Never<never>;
type NeverGuardGetsNeverWithCustomFallback = Utils.Guard.Never<never, string>;
type NeverGuardGetsAny = Utils.Guard.Never<any>;
type NeverGuardGetsUnknown = Utils.Guard.Never<unknown>;
type NeverGuardGetsNull = Utils.Guard.Never<null>;
type NeverGuardGetsUndefined = Utils.Guard.Never<undefined>;
// OfTypes
// Single Type
type OfTypesNeverGetsNeverWithFallback = Utils.Guard.OfTypes<[never], never, string>;
type OfTypesNeverGetsNeverWithoutFallback = Utils.Guard.OfTypes<[never], never>;
type OfTypesUndefined = Utils.Guard.OfTypes<[undefined], undefined>;
type OfTypesUndefinedGetsString = Utils.Guard.OfTypes<[undefined], string>;
type OfTypesNull = Utils.Guard.OfTypes<[null], null>;
type OfTypesNullGetsString = Utils.Guard.OfTypes<[null], string>;
type OfTypesUnknown = Utils.Guard.OfTypes<[unknown], unknown, null>;
type OfTypesUnknownGetString = Utils.Guard.OfTypes<[unknown], string>;
type OfTypeUnionGetsMatchingUnion = Utils.Guard.OfTypes<[string | number], string | number>;
type OfTypeUnionGetsUnionElement = Utils.Guard.OfTypes<[string | number], string>;
// OfTypes<[any]> catches any given value
type OfTypesAnyGetsAny = Utils.Guard.OfTypes<[any], any>;
type OfTypesAnyGetsString = Utils.Guard.OfTypes<[any], string>;
// Multiple Types
type OfTypesStringAndNumberGetsString = Utils.Guard.OfTypes<[string, number], string>;
type OfTypesStringAndNumberGetsNumber = Utils.Guard.OfTypes<[string, number], string>;
type OfTypesStringAndNumberGetsUnionOfStringNumber = Utils.Guard.OfTypes<
[string, number],
string | number
>;
type OfTypesStringAndNumberGetsBoolean = Utils.Guard.OfTypes<[string, number], boolean>;
export {
// Never
NeverGuardGetsNeverWithDefaultFallback,
NeverGuardGetsNeverWithCustomFallback,
NeverGuardGetsAny,
NeverGuardGetsUnknown,
NeverGuardGetsNull,
NeverGuardGetsUndefined,
// OfTypes
// Single Type
OfTypesNeverGetsNeverWithFallback,
OfTypesNeverGetsNeverWithoutFallback,
OfTypesAnyGetsAny,
OfTypesAnyGetsString,
OfTypesUndefined,
OfTypesUndefinedGetsString,
OfTypesNull,
OfTypesNullGetsString,
OfTypesUnknown,
OfTypesUnknownGetString,
OfTypeUnionGetsMatchingUnion,
OfTypeUnionGetsUnionElement,
// Multiple Types
OfTypesStringAndNumberGetsString,
OfTypesStringAndNumberGetsNumber,
OfTypesStringAndNumberGetsUnionOfStringNumber,
OfTypesStringAndNumberGetsBoolean,
};

View File

@ -0,0 +1,59 @@
import { Utils } from '../../..';
// Aux
type Base = { x: 'foo' | 'bar' };
type Obj = { foo: { x: 'foo' }; bar: { x: 'bar' }; other: { x: '42' } };
// KeysBy
type KeysByString = Utils.Object.KeysBy<{ foo: 'bar'; bar: 'foo'; foobar: 2 }, string>;
type KeysByNumber = Utils.Object.KeysBy<{ foo: 'bar'; bar: 'foo'; foobar: 2 }, number>;
type KeysByNever = Utils.Object.KeysBy<{ foo: 'bar'; bar: 'foo'; foobar: 2 }, never>;
type KeysByUnknown = Utils.Object.KeysBy<{ foo: 'bar'; bar: 'foo'; foobar: 2 }, unknown>;
type KeysByObj = Utils.Object.KeysBy<Obj, Base>;
// KeysExcept
type KeysExceptString = Utils.Object.KeysExcept<{ foo: 'bar'; bar: 'foo'; foobar: 2 }, string>;
type KeysExceptNumber = Utils.Object.KeysExcept<{ foo: 'bar'; bar: 'foo'; foobar: 2 }, number>;
type KeysExceptNever = Utils.Object.KeysExcept<{ foo: 'bar'; bar: 'foo'; foobar: 2 }, never>;
type KeysExceptUnknown = Utils.Object.KeysExcept<{ foo: 'bar'; bar: 'foo'; foobar: 2 }, unknown>;
type KeysExceptObj = Utils.Object.KeysExcept<Obj, Base>;
// PickBy
type PickByString = Utils.Object.PickBy<{ foo: 'bar'; bar: 'foo'; foobar: 2 }, string>;
type PickByNumber = Utils.Object.PickBy<{ foo: 'bar'; bar: 'foo'; foobar: 2 }, number>;
type PickByNever = Utils.Object.PickBy<{ foo: 'bar'; bar: 'foo'; foobar: 2 }, never>;
type PickByUnknown = Utils.Object.PickBy<{ foo: 'bar'; bar: 'foo'; foobar: 2 }, unknown>;
type PickByObj = Utils.Object.PickBy<Obj, Base>;
// Values
type Values = Utils.Object.Values<{ foo: 'bar'; bar: 'foo'; foobar: 2 }>;
type ValuesNever = Utils.Object.Values<never>;
type ValuesContainNever = Utils.Object.Values<{ foo: 'bar'; bar: 'foo'; foobar: never }>;
export {
// KeysBy
KeysByString,
KeysByNumber,
KeysByNever,
KeysByUnknown,
KeysByObj,
// KeysExcept
KeysExceptString,
KeysExceptNumber,
KeysExceptNever,
KeysExceptUnknown,
KeysExceptObj,
// PickBy
PickByString,
PickByNumber,
PickByNever,
PickByUnknown,
PickByObj,
// Values
Values,
ValuesNever,
ValuesContainNever,
};

View File

@ -0,0 +1,12 @@
import path from 'path';
import { fromFile } from 'ts-zen';
const DEFINITIONS_ROOT = path.join(__dirname, 'definitions');
export const createTypeSelector = (filePath: string) => {
// TODO: Remove when strapi/strapi is migrated to TS
return fromFile(path.join(DEFINITIONS_ROOT, filePath), {
compilerOptions: { strict: true },
ignoreProjectOptions: true,
});
};

View File

@ -0,0 +1,85 @@
import path from 'path';
import { AssertTypeSelector, t } from 'ts-zen';
import { createTypeSelector } from '../test.utils';
const DEFINITIONS_PATH = path.join('utils', 'array.d.ts');
let type: AssertTypeSelector<typeof import('../definitions/utils/array')>;
describe('Utils.Array', () => {
beforeAll(() => {
type = createTypeSelector(DEFINITIONS_PATH);
});
test('String Values', () => {
type('StringValues').isUnion([
t.stringLiteral('foo'),
t.stringLiteral('bar'),
t.stringLiteral('baz'),
]);
});
test('Mixed Values', () => {
const expectedResultType = [
t.anonymousObject({
properties: {
foo: t.stringLiteral('bar'),
baz: t.booleanLiteral(false),
prop: t.anonymousObject({
properties: {
foo: t.stringLiteral('bar'),
bar: t.stringLiteral('foo'),
},
}),
},
}),
t.numberLiteral(1),
t.stringLiteral('foo'),
t.booleanLiteral(true),
];
type('MixedValues').isUnion(expectedResultType);
// The result type should not contain the type 'never'
type('ContainsNever').isUnion(expectedResultType);
});
test('Number Values', () => {
type('NumberValues').isUnion([t.numberLiteral(1), t.numberLiteral(2), t.numberLiteral(3)]);
});
test('Bool Values', () => {
type('BoolValues').isBoolean();
type('TrueBoolLiteralValues').isBooleanLiteral(true);
type('FalseBoolLiteralValues').isBooleanLiteral(false);
});
test('Object Values', () => {
type('ObjectValues').isUnion([
t.anonymousObject({
properties: { prop1: t.booleanLiteral(true), prop2: t.booleanLiteral(false) },
}),
t.anonymousObject({
properties: {
foo: t.stringLiteral('bar'),
baz: t.booleanLiteral(false),
prop: t.anonymousObject({
properties: {
foo: t.stringLiteral('bar'),
bar: t.stringLiteral('foo'),
},
}),
},
}),
]);
});
test('Is Empty', () => {
type('IsEmptyWithEmptyTuple').isBooleanLiteral(true);
type('IsEmptyWithNotEmptyTuple').isBooleanLiteral(false);
});
test('Is Not Empty', () => {
type('IsNotEmptyWithNotEmptyTuple').isBooleanLiteral(true);
type('IsNotEmptyWithEmptyTuple').isBooleanLiteral(false);
});
});

View File

@ -0,0 +1,192 @@
import path from 'path';
import { t, AssertTypeSelector } from 'ts-zen';
import { createTypeSelector } from '../test.utils';
const DEFINITIONS_PATH = path.join('utils', 'expression.d.ts');
let type: AssertTypeSelector<typeof import('../definitions/utils/expression')>;
describe('Utils.Expression', () => {
beforeAll(() => {
type = createTypeSelector(DEFINITIONS_PATH);
});
test('Is Never', () => {
type('IsNeverGivenNever').isBooleanLiteral(true);
type('IsNeverNotGivenNever').isBooleanLiteral(false);
type('IsNeverGivenUnknown').isBooleanLiteral(false);
});
test('Is Not Never', () => {
type('IsNotNeverGivenNever').isBooleanLiteral(false);
type('IsNotNeverGivenUnknown').isBooleanLiteral(true);
type('IsNotNeverGivenString').isBooleanLiteral(true);
type('IsNotNeverGivenStringLiteral').isBooleanLiteral(true);
});
test('Is True', () => {
type('IsTrueGivenTrue').isBooleanLiteral(true);
type('IsTrueGivenFalse').isBooleanLiteral(false);
type('IsTrueGivenBoolean').isBooleanLiteral(false);
type('IsTrueGivenNonBoolean').isBooleanLiteral(false);
type('IsTrueGivenNonBooleanLiteral').isBooleanLiteral(false);
type('IsTrueGivenOne').isBooleanLiteral(false);
type('IsTrueGivenZero').isBooleanLiteral(false);
});
test('Is False', () => {
type('IsFalseGivenFalse').isBooleanLiteral(true);
type('IsFalseGivenTrue').isBooleanLiteral(false);
type('IsFalseGivenBoolean').isBooleanLiteral(false);
type('IsFalseGivenNonBoolean').isBooleanLiteral(false);
type('IsFalseGivenNonBooleanLiteral').isBooleanLiteral(false);
type('IsFalseGivenOne').isBooleanLiteral(false);
type('IsFalseGivenZero').isBooleanLiteral(false);
});
test('Is Strict Equal', () => {
type('IsStrictEqualGivenNever').isBooleanLiteral(true);
type('IsStrictEqualGivenNeverAndUnknown').isBooleanLiteral(false);
type('IsStrictEqualGivenEqualLiterals').isBooleanLiteral(true);
type('IsStrictEqualGivenEqualTypes').isBooleanLiteral(true);
type('IsStrictEqualGivenDifferentLiterals').isBooleanLiteral(false);
type('IsStrictEqualGivenDifferentTypes').isBooleanLiteral(false);
type('IsStrictEqualGivenStringAndStringLiteral').isBooleanLiteral(false);
type('IsStrictEqualGivenStringLiteralAndString').isBooleanLiteral(false);
type('IsStrictEqualGivenNumberAndNumberLiteral').isBooleanLiteral(false);
type('IsStrictEqualGivenNumberLiteralAndNumber').isBooleanLiteral(false);
});
test('Extends', () => {
// String
type('StringExtendsString').isBooleanLiteral(true);
type('StringLiteralExtendsString').isBooleanLiteral(true);
type('StringExtendsStringLiteral').isBooleanLiteral(false);
type('StringExtendsNumber').isBooleanLiteral(false);
type('StringLiteralExtendsNumber').isBooleanLiteral(false);
type('StringExtendsNumberLiteral').isBooleanLiteral(false);
// Number
type('NumberExtendsNumber').isBooleanLiteral(true);
type('NumberLiteralExtendsNumber').isBooleanLiteral(true);
type('NumberExtendsNumberLiteral').isBooleanLiteral(false);
type('NumberLiteralExtendsString').isBooleanLiteral(false);
type('NumberExtendsStringLiteral').isBooleanLiteral(false);
// Object
type('ObjectExtendsObject').isBooleanLiteral(true);
type('ObjectLiteralExtendsObject').isBooleanLiteral(true);
type('ObjectExtendsObjectLiteral').isBooleanLiteral(false);
type('ObjectExtendsNumber').isBooleanLiteral(false);
type('ObjectExtendsAny').isBooleanLiteral(true);
type('ObjectExtendsUnknown').isBooleanLiteral(true);
type('ObjectExtendsNever').isBooleanLiteral(false);
// Array
type('ArrayExtendsArray').isBooleanLiteral(true);
type('TupleExtendsArray').isBooleanLiteral(true);
type('StringArrayExtendsArray').isBooleanLiteral(true);
});
test('Not Extends', () => {
type('StringNotExtendsString').isBooleanLiteral(false);
type('StringLiteralNotExtendsString').isBooleanLiteral(false);
type('StringNotExtendsStringLiteral').isBooleanLiteral(true);
type('StringNotExtendsNumber').isBooleanLiteral(true);
type('StringLiteralNotExtendsNumber').isBooleanLiteral(true);
type('StringNotExtendsNumberLiteral').isBooleanLiteral(true);
type('NumberNotExtendsNumber').isBooleanLiteral(false);
type('NumberLiteralNotExtendsNumber').isBooleanLiteral(false);
type('NumberNotExtendsNumberLiteral').isBooleanLiteral(true);
type('NumberNotExtendsString').isBooleanLiteral(true);
type('NumberLiteralNotExtendsString').isBooleanLiteral(true);
type('NumberNotExtendsStringLiteral').isBooleanLiteral(true);
type('ObjectNotExtendsObject').isBooleanLiteral(false);
type('ObjectLiteralNotExtendsObject').isBooleanLiteral(false);
type('ObjectNotExtendsObjectLiteral').isBooleanLiteral(true);
type('ObjectNotExtendsNumber').isBooleanLiteral(true);
type('ObjectNotExtendsAny').isBooleanLiteral(false);
type('ObjectNotExtendsUnknown').isBooleanLiteral(false);
type('ObjectNotExtendsNever').isBooleanLiteral(true);
type('ArrayNotExtendsArray').isBooleanLiteral(false);
type('TupleNotExtendsArray').isBooleanLiteral(false);
type('StringArrayNotExtendsArray').isBooleanLiteral(false);
});
test('If', () => {
type('IfTrue').isBooleanLiteral(true);
type('IfFalse').isBooleanLiteral(false);
type('IfBoolean').isBooleanLiteral(false);
type('IfNumber').isBooleanLiteral(false);
type('IfString').isBooleanLiteral(false);
type('IfObject').isBooleanLiteral(false);
type('IfUnknown').isBooleanLiteral(false);
type('IfAny').isBooleanLiteral(true);
type('IfNever').isBooleanLiteral(true);
type('IfStringLiteral').isBooleanLiteral(false);
type('IfTuple').isBooleanLiteral(false);
type('IfArray').isBooleanLiteral(false);
type('IfStringArray').isBooleanLiteral(false);
type('IfTupleArray').isBooleanLiteral(false);
type('IfUnion').isBooleanLiteral(false);
type('IfIntersection').isBooleanLiteral(true);
type('IfFunction').isBooleanLiteral(false);
type('IfClass').isBooleanLiteral(false);
type('IfVoid').isBooleanLiteral(false);
type('IfNull').isBooleanLiteral(false);
type('IfUndefined').isBooleanLiteral(false);
type('IfWithStringReturnType').isStringLiteral('test');
type('IfWithNumberReturnType').isNumberLiteral(1);
type('IfWithBooleanReturnType').isBooleanLiteral(true);
type('IfWithObjectReturnType').isAnonymousObject({
properties: { foo: t.numberLiteral(1) },
});
type('IfWithTupleReturnType').isTuple([
t.numberLiteral(1),
t.numberLiteral(2),
t.numberLiteral(3),
]);
type('IfWithArrayReturnType').isArray(t.string());
type('IfWithUnionReturnType').isUnion([t.string(), t.number()]);
type('IfWithVoidReturnType').isVoid();
type('IfWithNullReturnType').isNull();
type('IfWithUndefinedReturnType').isUndefined();
type('IfWithNeverReturnType').isNever();
type('IfWithUnknownReturnType').isUnknown();
type('IfWithAnyReturnType').isAny();
});
test('MatchFirst', () => {
type('MatchFirstReturnsTestSuccessful').isStringLiteral('test-successful');
type('MatchFirstReturnsDefault').isStringLiteral('default');
type('MatchFirstReturnsNeverWithEmptyTests').isNever();
type('MatchFirstReturnsNeverWithNeverDefault').isNever();
});
test('MatchAllIntersect', () => {
type('MatchAllIntersectWithOneTrueCondition').isAnonymousObject({
properties: {
test: t.numberLiteral(1),
},
});
type('MatchAllIntersectWithAllFalseConditions').isUnknown();
type('MatchAllIntersectWithIntersection').isStringLiteral('test');
});
test('Test', () => {
type('TestPasses').isTuple([t.booleanLiteral(true), t.stringLiteral('test')]);
type('TestFails').isTuple([t.booleanLiteral(false), t.stringLiteral('test')]);
});
test('And', () => {
type('AndTrue').isBooleanLiteral(true);
type('AndFalse').isBooleanLiteral(false);
});
test('Or', () => {
type('OrTrue').isBooleanLiteral(true);
type('OrFalse').isBooleanLiteral(false);
type('OrTrueFalse').isBooleanLiteral(true);
});
});

View File

@ -0,0 +1,52 @@
import path from 'path';
import { AssertTypeSelector, t } from 'ts-zen';
import { createTypeSelector } from '../test.utils';
const DEFINITIONS_PATH = path.join('utils', 'guard.d.ts');
let type: AssertTypeSelector<typeof import('../definitions/utils/guard')>;
describe('Utils.Guard', () => {
beforeAll(() => {
type = createTypeSelector(DEFINITIONS_PATH);
});
test('Never Guard', () => {
type('NeverGuardGetsNeverWithDefaultFallback').isUnknown();
type('NeverGuardGetsNeverWithCustomFallback').isString();
type('NeverGuardGetsAny').isAny();
type('NeverGuardGetsNull').isNull();
type('NeverGuardGetsUndefined').isUndefined();
type('NeverGuardGetsUnknown').isUnknown();
});
describe('OfTypes', () => {
test('Single Type', () => {
type('OfTypesNeverGetsNeverWithFallback').isString();
type('OfTypesNeverGetsNeverWithoutFallback').isUnknown();
type('OfTypesUndefined').isUnknown();
type('OfTypesUndefinedGetsString').isString();
type('OfTypesNull').isUnknown();
type('OfTypesNullGetsString').isString();
type('OfTypesUnknown').isNull();
type('OfTypesUnknownGetString').isString();
type('OfTypesAnyGetsAny').isUnknown();
type('OfTypesAnyGetsString').isUnknown();
type('OfTypeUnionGetsMatchingUnion').isUnknown();
type('OfTypeUnionGetsUnionElement').isString();
});
test('Multiple Type Guard', () => {
type('OfTypesStringAndNumberGetsString').isUnknown();
type('OfTypesStringAndNumberGetsNumber').isUnknown();
type('OfTypesStringAndNumberGetsUnionOfStringNumber').isUnion([t.string(), t.number()]);
type('OfTypesStringAndNumberGetsBoolean').isBoolean();
});
});
});

View File

@ -0,0 +1,74 @@
import path from 'path';
import { t, AssertTypeSelector } from 'ts-zen';
import type ObjectUtils from '../definitions/utils/object';
import { createTypeSelector } from '../test.utils';
const DEFINITIONS_PATH = path.join('utils', 'object.d.ts');
let type: AssertTypeSelector<typeof ObjectUtils>;
describe('Utils.Object', () => {
beforeAll(() => {
type = createTypeSelector(DEFINITIONS_PATH);
});
test('KeysBy', () => {
type('KeysByString').isUnion([t.stringLiteral('foo'), t.stringLiteral('bar')]);
type('KeysByNumber').isStringLiteral('foobar');
type('KeysByNever').isNever();
type('KeysByUnknown').isUnion([
t.stringLiteral('foo'),
t.stringLiteral('bar'),
t.stringLiteral('foobar'),
]);
type('KeysByObj').isUnion([t.stringLiteral('foo'), t.stringLiteral('bar')]);
});
test('KeysExcept', () => {
type('KeysExceptString').isStringLiteral('foobar');
type('KeysExceptNumber').isUnion([t.stringLiteral('foo'), t.stringLiteral('bar')]);
type('KeysExceptNever').isUnion([
t.stringLiteral('foo'),
t.stringLiteral('bar'),
t.stringLiteral('foobar'),
]);
type('KeysExceptUnknown').isNever();
type('KeysExceptObj').isStringLiteral('other');
});
test('PickBy', () => {
type('PickByString').isMappedType({
properties: {
foo: t.stringLiteral('bar'),
bar: t.stringLiteral('foo'),
},
});
type('PickByNumber').isMappedType({
properties: {
foobar: t.numberLiteral(2),
},
});
type('PickByNever').isMappedType({
properties: {},
});
type('PickByUnknown').isMappedType({
properties: {
foobar: t.numberLiteral(2),
foo: t.stringLiteral('bar'),
bar: t.stringLiteral('foo'),
},
});
type('PickByObj').isMappedType({
properties: {
foo: t.mappedType({ properties: { x: t.stringLiteral('bar') } }),
bar: t.mappedType({ properties: { x: t.stringLiteral('foo') } }),
},
});
});
test('Values', () => {
type('Values').isUnion([t.stringLiteral('foo'), t.stringLiteral('bar'), t.numberLiteral(2)]);
type('ValuesNever').isNever();
type('ValuesContainNever').isUnion([t.stringLiteral('foo'), t.stringLiteral('bar')]);
});
});

View File

@ -1,65 +1,73 @@
import path from 'path';
import { fromFile, t, AssertTypeSelector } from 'ts-zen';
const STRING_UTILS_DTS_PATH = path.join(__dirname, '..', 'definitions', 'utils', 'string.d.ts');
import { AssertTypeSelector, t } from 'ts-zen';
let assertType: AssertTypeSelector;
import { createTypeSelector } from '../test.utils';
import type StringUtils from '../definitions/utils/string';
const DEFINITIONS_PATH = path.join('utils', 'string.d.ts');
let type: AssertTypeSelector<typeof StringUtils>;
describe('Utils.String', () => {
beforeAll(() => {
assertType = fromFile(STRING_UTILS_DTS_PATH, {
compilerOptions: { strict: true },
ignoreProjectOptions: true,
});
type = createTypeSelector(DEFINITIONS_PATH);
});
test('Dict', () => {
// TODO: Replace with isMappedType matcher when available
assertType('NumberDict').equals('{ [x: string]: number; }');
assertType('StringDict').equals('{ [x: string]: string; }');
assertType('BooleanDict').equals('{ [x: string]: boolean; }');
type('NumberDict').isAnonymousObject({
indexes: [{ keyType: t.string(), type: t.number() }],
});
type('StringDict').isAnonymousObject({
indexes: [{ keyType: t.string(), type: t.string() }],
});
type('BooleanDict').isAnonymousObject({
indexes: [{ keyType: t.string(), type: t.boolean() }],
});
});
test('EndsWith', () => {
assertType('EndsWithCorrectNumber').isBooleanLiteral(true);
assertType('EndsWithIncorrectNumber').isBooleanLiteral(false);
assertType('EndsWithCorrectString').isBooleanLiteral(true);
assertType('EndsWithIncorrectString').isBooleanLiteral(false);
type('EndsWithCorrectNumber').isBooleanLiteral(true);
type('EndsWithIncorrectNumber').isBooleanLiteral(false);
type('EndsWithCorrectString').isBooleanLiteral(true);
type('EndsWithIncorrectString').isBooleanLiteral(false);
});
test('StartsWith', () => {
assertType('StartsWithCorrectNumber').isBooleanLiteral(true);
assertType('StartsWithIncorrectNumber').isBooleanLiteral(false);
assertType('StartsWithCorrectString').isBooleanLiteral(true);
assertType('StartsWithIncorrectString').isBooleanLiteral(false);
type('StartsWithCorrectNumber').isBooleanLiteral(true);
type('StartsWithIncorrectNumber').isBooleanLiteral(false);
type('StartsWithCorrectString').isBooleanLiteral(true);
type('StartsWithIncorrectString').isBooleanLiteral(false);
});
test('Includes', () => {
const template = (str: string | number | boolean) => [t.string(), String(str), t.string()];
assertType('IncludesNumber').isTemplateLiteral(template(42));
assertType('IncludesString').isTemplateLiteral(template('foo'));
assertType('IncludesBoolean').isUnion([
type('IncludesNumber').isTemplateLiteral(template(42));
type('IncludesString').isTemplateLiteral(template('foo'));
type('IncludesBoolean').isUnion([
t.templateLiteral(template(true)),
t.templateLiteral(template(false)),
]);
assertType('IncludesBooleanLiteral').isTemplateLiteral(template(true));
type('IncludesBooleanLiteral').isTemplateLiteral(template(true));
});
test('NonEmpty', () => {
assertType('NonEmptyOnEmptyString').isNever();
assertType('NonEmptyOnNonEmptyString').isStringLiteral('Hello World');
type('NonEmptyOnEmptyString').isNever();
type('NonEmptyOnNonEmptyString').isStringLiteral('Hello World');
});
test('Prefix', () => {
assertType('PrefixEmptyString').isStringLiteral('Hello');
assertType('PrefixString').isTemplateLiteral(['Hello ', t.string()]);
assertType('PrefixLiteralString').isStringLiteral('Hello World');
assertType('PrefixLiteralStringUnion').isUnion([
type('PrefixEmptyString').isStringLiteral('Hello');
type('PrefixString').isTemplateLiteral(['Hello ', t.string()]);
type('PrefixLiteralString').isStringLiteral('Hello World');
type('PrefixLiteralStringUnion').isUnion([
t.stringLiteral('Hello World'),
t.stringLiteral('Hello Everyone'),
]);
assertType('PrefixLiteralStringWithUnion').isUnion([
type('PrefixLiteralStringWithUnion').isUnion([
t.stringLiteral('Hello World'),
t.stringLiteral('Bonjour World'),
t.stringLiteral('Hola World'),
@ -67,33 +75,31 @@ describe('Utils.String', () => {
});
test('Suffix', () => {
assertType('SuffixEmptyString').isStringLiteral('Hello');
assertType('SuffixString').isTemplateLiteral([t.string(), '.']);
assertType('SuffixLiteralString').isStringLiteral('Hello World');
assertType('SuffixLiteralStringUnion').isUnion([
type('SuffixEmptyString').isStringLiteral('Hello');
type('SuffixString').isTemplateLiteral([t.string(), '.']);
type('SuffixLiteralString').isStringLiteral('Hello World');
type('SuffixLiteralStringUnion').isUnion([
t.stringLiteral('Hello World'),
t.stringLiteral('Bonjour World'),
t.stringLiteral('Hola World'),
]);
assertType('SuffixLiteralStringWithUnion').isUnion([
type('SuffixLiteralStringWithUnion').isUnion([
t.stringLiteral('Hello World'),
t.stringLiteral('Hello Everyone'),
]);
});
test('Literal', () => {
assertType('Literal').isUnion([t.string(), t.number(), t.bigInt(), t.booleanLiteral()]);
type('Literal').isUnion([t.string(), t.number(), t.bigInt(), t.booleanLiteral()]);
});
test('Split', () => {
assertType('SplitEmptyStringBySpace').isTuple([]);
assertType('SplitEmptyStringByEmptyString').isTuple([]);
assertType('SplitEmptyStringByString').isTuple([]);
assertType('SplitBySpace').isTuple(
['Hello', 'World,', 'How', 'are', 'you?'].map(t.stringLiteral)
);
assertType('SplitByEmptyString').isTuple(['H', 'e', 'l', 'l', 'o'].map(t.stringLiteral));
type('SplitEmptyStringBySpace').isTuple([]);
type('SplitEmptyStringByEmptyString').isTuple([]);
type('SplitEmptyStringByString').isTuple([]);
type('SplitBySpace').isTuple(['Hello', 'World,', 'How', 'are', 'you?'].map(t.stringLiteral));
type('SplitByEmptyString').isTuple(['H', 'e', 'l', 'l', 'o'].map(t.stringLiteral));
// This will use any string character as a delimiter, thus removing 1/2 characters
assertType('SplitByString').isTuple(['H', 'l', 'o'].map(t.stringLiteral));
type('SplitByString').isTuple(['H', 'l', 'o'].map(t.stringLiteral));
});
});

View File

@ -38,9 +38,9 @@ export type Suffix<TValue extends string, TSuffix extends Literal> = `${TValue}$
export type Prefix<TValue extends string, TPrefix extends Literal> = `${TPrefix}${TValue}`;
/**
* Creates a record where every key is a string and every value is `T`
* Creates an indexed object where every key is a string and every value is `T`
*/
export type Dict<T> = Record<string, T>;
export type Dict<T> = { [key: string]: T };
/**
* Checks if a given string ends with the given literal

View File

@ -7834,6 +7834,7 @@ __metadata:
semver: 7.5.4
statuses: 2.0.1
supertest: 6.3.3
ts-zen: "https://github.com/strapi/ts-zen#66e02232f5997674cc7032ea3ee59d9864863732"
tsconfig: 4.13.7
typescript: 5.2.2
vite: 4.4.9