Compare commits
10 Commits
0e7238998d
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5199ef343e | ||
|
|
c11151e9ab | ||
|
|
31f267bb98 | ||
|
|
8cc7db94df | ||
|
|
79d365b3d8 | ||
|
|
9cb4bc2f06 | ||
|
|
eba47b51bd | ||
|
|
9170f6856a | ||
|
|
97a517737a | ||
|
|
434d91ff9d |
45
CHANGELOG.md
45
CHANGELOG.md
@@ -2,6 +2,51 @@
|
|||||||
All notable changes to this project will be documented in this file. See [conventional commits](https://www.conventionalcommits.org/) for commit guidelines.
|
All notable changes to this project will be documented in this file. See [conventional commits](https://www.conventionalcommits.org/) for commit guidelines.
|
||||||
|
|
||||||
- - -
|
- - -
|
||||||
|
## v3.4.0 - 2025-10-03
|
||||||
|
### Package updates
|
||||||
|
- typescript-config bumped to typescript-config-v1.1.0
|
||||||
|
- core-extensions bumped to core-extensions-v2.3.0
|
||||||
|
- prettier-config bumped to prettier-config-v1.1.0
|
||||||
|
### Global changes
|
||||||
|
#### Bug Fixes
|
||||||
|
- **(typescript-types)** Handle union types when filtering - (8d51cb7) - *trbernstein*
|
||||||
|
- **(typescript-types)** Prevent distribution in IsKeyOf<T,K> - (96cf6bb) - *trbernstein*
|
||||||
|
- **(typescript-types)** Export types s.t. they're usable - (520a690) - *trbernstein*
|
||||||
|
#### Build system
|
||||||
|
- **(editor)** Install typescript in workspace root for Nova - (7decf5e) - *trbernstein*
|
||||||
|
#### Documentation
|
||||||
|
- **(typescript-types)** Add missing links in README - (f7cde63) - *trbernstein*
|
||||||
|
#### Features
|
||||||
|
- **(typescript-types)** Add IsAny<T> - (5454e90) - *trbernstein*
|
||||||
|
- **(typescript-types)** Add IsNever<T> - (afe7bee) - *trbernstein*
|
||||||
|
- **(typescript-types)** Add If<Test,TrueBranch,FalseBranch> - (319ccac) - *trbernstein*
|
||||||
|
- **(typescript-types)** Add OptionalKeysOf<T> - (4807617) - *trbernstein*
|
||||||
|
- **(typescript-types)** Add Assign<Shape,Defaults,Obj> - (610e7a0) - *trbernstein*
|
||||||
|
- **(typescript-types)** Add test for any and never in OptionalKeysOf - (a7bc60f) - *trbernstein*
|
||||||
|
- **(typescript-types)** Add isKeyOf<T,K> - (bb706e7) - *trbernstein*
|
||||||
|
- **(typescript-types)** Add IsEmptyKey<S> - (0cbc6d4) - *trbernstein*
|
||||||
|
- **(typescript-types)** Add KeyPath<T, Separator = '.'> - (eac7768) - *trbernstein*
|
||||||
|
- **(typescript-types)** Remove optionality of keys in Assign - (9700b7d) - *trbernstein*
|
||||||
|
- **(typescript-types)** Add Simplify<T, E, I> - (2aa71d9) - *trbernstein*
|
||||||
|
- **(typescript-types)** Make Assign assignable to Generics by removing optionals - (4ec764d) - *trbernstein*
|
||||||
|
- **(typescript-types)** Add KeyPaths<T, F, O> - (3997eaa) - *trbernstein*
|
||||||
|
- **(typescript-types)** Add IsUnknown<T> - (527250a) - *trbernstein*
|
||||||
|
- **(typescript-types)** Fully rewrite KeyPaths<T,O,F> - (c3238a6) - *trbernstein*
|
||||||
|
- **(typescript-types)** Add Get<O, P> - (ac53d1e) - *trbernstein*
|
||||||
|
- **(typescript-types)** Allow inversion of filter in KeyPaths<T, O, F> - (0e72389) - *trbernstein*
|
||||||
|
- **(typescript-types)** Fully rewrite KeyPaths<T,O,F> - (434d91f) - *trbernstein*
|
||||||
|
- **(typescript-types)** Allow inversion of filter in KeyPaths<T, O, F> - (97a5177) - *trbernstein*
|
||||||
|
- **(typescript-types)** Fully rewrite KeyPaths<T,O,F> - (eba47b5) - *trbernstein*
|
||||||
|
- **(typescript-types)** Adapt Get to KeyPaths and KeyPath changes - (9cb4bc2) - *trbernstein*
|
||||||
|
#### Miscellaneous Chores
|
||||||
|
- Update pnpm - (3c17970) - trbernstein
|
||||||
|
#### Refactoring
|
||||||
|
- **(typescript-types)** Refine KeyPaths<T,O,F> logic - (5461fc5) - *trbernstein*
|
||||||
|
#### Tests
|
||||||
|
- **(typescript-types)** Test optional key path transformation - (9170f68) - *trbernstein*
|
||||||
|
|
||||||
|
- - -
|
||||||
|
|
||||||
## v3.3.1 - 1970-01-01
|
## v3.3.1 - 1970-01-01
|
||||||
### Package updates
|
### Package updates
|
||||||
- core-extensions bumped to core-extensions-v2.2.1
|
- core-extensions bumped to core-extensions-v2.2.1
|
||||||
|
|||||||
@@ -2,6 +2,14 @@
|
|||||||
All notable changes to this project will be documented in this file. See [conventional commits](https://www.conventionalcommits.org/) for commit guidelines.
|
All notable changes to this project will be documented in this file. See [conventional commits](https://www.conventionalcommits.org/) for commit guidelines.
|
||||||
|
|
||||||
- - -
|
- - -
|
||||||
|
## core-extensions-v2.3.0 - 2025-10-03
|
||||||
|
#### Features
|
||||||
|
- **(core-extensions)** Handle function type in MappedKeys - (8cc7db9) - *trbernstein*
|
||||||
|
#### Refactoring
|
||||||
|
- **(core-extensions)** Use tab for identenion - (79d365b) - *trbernstein*
|
||||||
|
|
||||||
|
- - -
|
||||||
|
|
||||||
## core-extensions-v2.2.1 - 2025-07-04
|
## core-extensions-v2.2.1 - 2025-07-04
|
||||||
#### Bug Fixes
|
#### Bug Fixes
|
||||||
- **(core-extensions)** Apply mapKeys only to plain objects - (0d52c33) - *trbernstein*
|
- **(core-extensions)** Apply mapKeys only to plain objects - (0d52c33) - *trbernstein*
|
||||||
|
|||||||
@@ -1,49 +1,49 @@
|
|||||||
{
|
{
|
||||||
"name": "@tabshift/core-extensions",
|
"name": "@tabshift/core-extensions",
|
||||||
"description": "Extensions to JavaScript core objects",
|
"description": "Extensions to JavaScript core objects",
|
||||||
"version": "2.2.1",
|
"version": "2.3.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "rollup -c && node scripts/generate-exports.js",
|
"build": "rollup -c && node scripts/generate-exports.js",
|
||||||
"prepublish": "pnpm build"
|
"prepublish": "pnpm build"
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-alias": "^5.1.1",
|
"@rollup/plugin-alias": "^5.1.1",
|
||||||
"@rollup/plugin-node-resolve": "^16.0.1",
|
"@rollup/plugin-node-resolve": "^16.0.1",
|
||||||
"@rollup/plugin-typescript": "^12.1.2",
|
"@rollup/plugin-typescript": "^12.1.2",
|
||||||
"@tabshift/typescript-config": "workspace:",
|
"@tabshift/typescript-config": "workspace:",
|
||||||
"fast-glob": "^3.3.3",
|
"fast-glob": "^3.3.3",
|
||||||
"rollup": "^4.40.0",
|
"rollup": "^4.40.0",
|
||||||
"rollup-plugin-dts": "^6.2.1",
|
"rollup-plugin-dts": "^6.2.1",
|
||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"type-fest": "^4.41.0",
|
"type-fest": "^4.41.0",
|
||||||
"typescript": "latest",
|
"typescript": "latest",
|
||||||
"vitest": "^3.1.2"
|
"vitest": "^3.1.2"
|
||||||
},
|
},
|
||||||
"author": "T. R. Bernstein <ljspkgs01-project@tabshift.dev>",
|
"author": "T. R. Bernstein <ljspkgs01-project@tabshift.dev>",
|
||||||
"license": "EUPL-1.2",
|
"license": "EUPL-1.2",
|
||||||
"exports": {
|
"exports": {
|
||||||
"./object/map-keys.f": {
|
"./object/map-keys.f": {
|
||||||
"import": "./dist/object/map-keys.f.js",
|
"import": "./dist/object/map-keys.f.js",
|
||||||
"types": "./dist/object/map-keys.f.d.ts"
|
"types": "./dist/object/map-keys.f.d.ts"
|
||||||
},
|
},
|
||||||
"./object/map-keys": {
|
"./object/map-keys": {
|
||||||
"import": "./dist/object/map-keys.js",
|
"import": "./dist/object/map-keys.js",
|
||||||
"types": "./dist/object/map-keys.d.ts"
|
"types": "./dist/object/map-keys.d.ts"
|
||||||
},
|
},
|
||||||
"./object/map-values.f": {
|
"./object/map-values.f": {
|
||||||
"import": "./dist/object/map-values.f.js",
|
"import": "./dist/object/map-values.f.js",
|
||||||
"types": "./dist/object/map-values.f.d.ts"
|
"types": "./dist/object/map-values.f.d.ts"
|
||||||
},
|
},
|
||||||
"./object/map-values": {
|
"./object/map-values": {
|
||||||
"import": "./dist/object/map-values.js",
|
"import": "./dist/object/map-values.js",
|
||||||
"types": "./dist/object/map-values.d.ts"
|
"types": "./dist/object/map-values.d.ts"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
export type MappedKeys<T, R extends string> = T extends any[]
|
export type MappedKeys<T, R extends string> = T extends Function
|
||||||
? { [K in keyof T]: MappedKeys<T[K], R> }
|
? T
|
||||||
: T extends object
|
: T extends Array<infer U>
|
||||||
? {
|
? Array<MappedKeys<U, R>>
|
||||||
[K in keyof T as R]: MappedKeys<T[K], R>
|
: T extends object
|
||||||
}
|
? {
|
||||||
: T
|
[K in keyof T as R]: MappedKeys<T[K], R>
|
||||||
|
}
|
||||||
|
: T
|
||||||
|
|||||||
11
packages/prettier-config/CHANGELOG.md
Normal file
11
packages/prettier-config/CHANGELOG.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# Changelog
|
||||||
|
All notable changes to this project will be documented in this file. See [conventional commits](https://www.conventionalcommits.org/) for commit guidelines.
|
||||||
|
|
||||||
|
- - -
|
||||||
|
## prettier-config-v1.1.0 - 2025-10-03
|
||||||
|
#### Features
|
||||||
|
- **(prettier-config)** Use tabs in base config - (31f267b) - *trbernstein*
|
||||||
|
|
||||||
|
- - -
|
||||||
|
|
||||||
|
Changelog generated by [cocogitto](https://github.com/cocogitto/cocogitto).
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
// Documentation for this file: https://prettier.io/en/configuration.html
|
// Documentation for this file: https://prettier.io/en/configuration.html
|
||||||
const config = {
|
const config = {
|
||||||
printWidth: 110,
|
printWidth: 110,
|
||||||
|
useTabs: true,
|
||||||
semi: false,
|
semi: false,
|
||||||
endOfLine: 'auto',
|
endOfLine: 'auto',
|
||||||
singleQuote: true,
|
singleQuote: true,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@tabshift/prettier-config",
|
"name": "@tabshift/prettier-config",
|
||||||
"description": "Shared Prettier config of Tabshift",
|
"description": "Shared Prettier config of Tabshift",
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"exports": "./base.js",
|
"exports": "./base.js",
|
||||||
"author": "T. R. Bernstein <ljspkgs01-project@tabshift.dev>",
|
"author": "T. R. Bernstein <ljspkgs01-project@tabshift.dev>",
|
||||||
|
|||||||
11
packages/typescript-config/CHANGELOG.md
Normal file
11
packages/typescript-config/CHANGELOG.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# Changelog
|
||||||
|
All notable changes to this project will be documented in this file. See [conventional commits](https://www.conventionalcommits.org/) for commit guidelines.
|
||||||
|
|
||||||
|
- - -
|
||||||
|
## typescript-config-v1.1.0 - 2025-10-03
|
||||||
|
#### Features
|
||||||
|
- **(typescript-config)** Allow synthetic default imports in base config - (c11151e) - *trbernstein*
|
||||||
|
|
||||||
|
- - -
|
||||||
|
|
||||||
|
Changelog generated by [cocogitto](https://github.com/cocogitto/cocogitto).
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"newLine": "LF",
|
"newLine": "LF",
|
||||||
"removeComments": true,
|
"removeComments": true,
|
||||||
"allowSyntheticDefaultImports": false,
|
"allowSyntheticDefaultImports": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
{
|
{
|
||||||
"name": "@tabshift/typescript-config",
|
"name": "@tabshift/typescript-config",
|
||||||
"description": "Shared TypeScript config of Tabshift",
|
"description": "Shared TypeScript config of Tabshift",
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"files": ["*.json"],
|
"files": [
|
||||||
|
"*.json"
|
||||||
|
],
|
||||||
"author": "T. R. Bernstein <ljspkgs01-project@tabshift.dev>",
|
"author": "T. R. Bernstein <ljspkgs01-project@tabshift.dev>",
|
||||||
"license": "EUPL-1.2"
|
"license": "EUPL-1.2"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,10 +18,26 @@ The types included in this library are categorized by their purpose.
|
|||||||
[`BuiltIns`]: src/built-ins.ts
|
[`BuiltIns`]: src/built-ins.ts
|
||||||
[`NonContainerType`]: src/non-container-type.ts
|
[`NonContainerType`]: src/non-container-type.ts
|
||||||
|
|
||||||
|
#### Boolean Operator Types
|
||||||
|
|
||||||
|
| Type | Description |
|
||||||
|
| --------------- | -------------------------------------------------------------------------- |
|
||||||
|
| [`And<A, B>`][] | `true` if `A` and `B` (both extend `boolean`) are both `true`. |
|
||||||
|
| [`Not<A>`][] | `true` if `A` (extends `boolean`) is `false` and vice-versa. |
|
||||||
|
| [`Or<A>`][] | `true` if either `A` or `B` (both extend `boolean`) is `true`. |
|
||||||
|
| [`Xor<A, B>`][] | `true` if `A` and `B` (both extend `boolean`) are not both `false`/`true`. |
|
||||||
|
|
||||||
|
[`And<A, B>`]: src/and.ts
|
||||||
|
[`Not<A>`]: src/not.ts
|
||||||
|
[`Or<A>`]: src/or.ts
|
||||||
|
[`Xor<A, B>`]: src/xor.ts
|
||||||
|
|
||||||
#### Test Types
|
#### Test Types
|
||||||
|
|
||||||
| Type | Description |
|
| Type | Description |
|
||||||
| --------------------------------------- | --------------------------------------------------------------------------------------------------------- |
|
| --------------------------------------- | --------------------------------------------------------------------------------------------------------- |
|
||||||
|
| [`Extends<A, B>`][] | `true` if `A` extends `B`, `false` otherwise[^extends_remark]. |
|
||||||
|
| [`ExtendsExactly<A, B>`][] | `true` if `A` extends `B`, `false` otherwise[^extends-exactly_remark]. |
|
||||||
| [`IsAny<T>`][] | `true` if `T` is `any`, `false` otherwise (`null`, `undefined` also yield `false`) |
|
| [`IsAny<T>`][] | `true` if `T` is `any`, `false` otherwise (`null`, `undefined` also yield `false`) |
|
||||||
| [`IsNever<T>`][] | `true` if `T` is `never`, `false` otherwise (`null`, `undefined`, `any` also yield `false`) |
|
| [`IsNever<T>`][] | `true` if `T` is `never`, `false` otherwise (`null`, `undefined`, `any` also yield `false`) |
|
||||||
| [`IsUndefined<T>`][] | `true` if `T` is `undefined`, `false` otherwise (`null`, `never`, `any` also yield `false`). |
|
| [`IsUndefined<T>`][] | `true` if `T` is `undefined`, `false` otherwise (`null`, `never`, `any` also yield `false`). |
|
||||||
@@ -30,10 +46,12 @@ The types included in this library are categorized by their purpose.
|
|||||||
| [`IsKeyOf<T, K>`][] | `true` if `K` is a key of `T`, `false` otherwise. If `T` is `any`, any `K` but `never` will yield `true`. |
|
| [`IsKeyOf<T, K>`][] | `true` if `K` is a key of `T`, `false` otherwise. If `T` is `any`, any `K` but `never` will yield `true`. |
|
||||||
| [`IsEmptyString<S>`][] | `true` if `S` is the empty string `''`, `false` otherwise.[^is-empty-string_remark] |
|
| [`IsEmptyString<S>`][] | `true` if `S` is the empty string `''`, `false` otherwise.[^is-empty-string_remark] |
|
||||||
|
|
||||||
[^if_remark]: If `boolean` is passed as `Test` the return value is a union of both branches, i.e. `TrueBranch | FalseBranch`.
|
[^extends_remark]: The result may be `boolean` (aka `true | false`) if `A` or `B` is a union type and the union members evaluate to different boolean results.
|
||||||
|
|
||||||
[^is-empty-string_remark]: If `T` is `any` will yield `true`, as it is taken to be `any` string.
|
[^extends-exactly_remark]: The extends check is done non-distributively in case `A` or `B` is a union type.
|
||||||
|
|
||||||
|
[`Extends<A, B>`]: src/extends.ts
|
||||||
|
[`ExtendsExactly<A, B>`]: src/extends-exactly.ts
|
||||||
[`IsAny<T>`]: src/is-any.ts
|
[`IsAny<T>`]: src/is-any.ts
|
||||||
[`IsNever<T>`]: src/is-never.ts
|
[`IsNever<T>`]: src/is-never.ts
|
||||||
[`IsUndefined<T>`]: src/is-undefined.ts
|
[`IsUndefined<T>`]: src/is-undefined.ts
|
||||||
@@ -70,8 +88,10 @@ The types included in this library are categorized by their purpose.
|
|||||||
|
|
||||||
#### Combination Types
|
#### Combination Types
|
||||||
|
|
||||||
| Type | Description |
|
| Type | Description |
|
||||||
| ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
|
| --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
|
||||||
| [`Assign<Shape, Defaults, Obj>`][] | Return a type with the structure of `Shape` and value types from `Obj` or `Default` for missing optional keys in `Obj`. |
|
| [`Assign<Shape, Defaults, Obj>`][] | Return a type with the structure of `Shape` and value types from `Obj` or `Default` for missing optional keys in `Obj`. |
|
||||||
|
| [`Concat<Prefix, Suffix, Separator>`][] | Concatenate `Prefix` and `Suffix` and - if both are non empty strings - separate them using `Separator`. |
|
||||||
|
|
||||||
[`Assign<Shape, Defaults, Obj>`]: src/assign.ts
|
[`Assign<Shape, Defaults, Obj>`]: src/assign.ts
|
||||||
|
[`Concat<Prefix, Suffix, Separator>`]: src/concat.ts
|
||||||
|
|||||||
5
packages/typescript-types/src/and.ts
Normal file
5
packages/typescript-types/src/and.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export type And<A extends boolean, B extends boolean> = A extends true
|
||||||
|
? B extends true
|
||||||
|
? true
|
||||||
|
: false
|
||||||
|
: false
|
||||||
9
packages/typescript-types/src/concat.ts
Normal file
9
packages/typescript-types/src/concat.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import type { If } from './if.js'
|
||||||
|
import type { Or } from './or.js'
|
||||||
|
import type { IsEmptyString } from './is-empty-string.js'
|
||||||
|
|
||||||
|
export type Concat<Prefix extends string, Suffix extends string, Separator extends string = ''> = If<
|
||||||
|
Or<IsEmptyString<Prefix>, IsEmptyString<Suffix>>,
|
||||||
|
`${Prefix}${Suffix}`,
|
||||||
|
`${Prefix}${Separator}${Suffix}`
|
||||||
|
>
|
||||||
1
packages/typescript-types/src/extends-exactly.ts
Normal file
1
packages/typescript-types/src/extends-exactly.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export type ExtendsExactly<A, B> = [A] extends [B] ? true : false
|
||||||
1
packages/typescript-types/src/extends.ts
Normal file
1
packages/typescript-types/src/extends.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export type Extends<A, B> = A extends B ? true : false
|
||||||
@@ -1,25 +1,30 @@
|
|||||||
import type { If } from './if.js'
|
|
||||||
import type { KeyPath } from './key-path.js'
|
import type { KeyPath } from './key-path.js'
|
||||||
import type { KeyPaths } from './key-paths.js'
|
import type { NonContainerType } from './non-container-type.js'
|
||||||
|
import type { KeyPaths, KeyPaths_Options } from './key-paths.js'
|
||||||
|
|
||||||
type FilterUndefined<T> = T extends undefined ? never : T
|
type ExtractFromForKey<Obj, Rest, ResultType = never> = Obj extends unknown
|
||||||
type FilterNull<T> = T extends null ? never : T
|
? never
|
||||||
type FilterUndefinedAndNull<T> = FilterUndefined<FilterNull<T>>
|
: Obj extends undefined
|
||||||
|
? never
|
||||||
|
: Obj extends NonContainerType
|
||||||
|
? Obj | ResultType
|
||||||
|
: GetByKeyPath<Obj, Rest, ResultType>
|
||||||
|
|
||||||
type ExtractFromForKey<Obj, Key, Rest> = Key extends keyof Obj
|
type GetByKeyPath<Obj, Keys, ResultType = never> = Keys extends []
|
||||||
? Obj[Key] extends Record<PropertyKey, unknown>
|
? Obj
|
||||||
? GetByKeyPath<Obj[Key], Rest>
|
: Keys extends [infer Key, ...infer Rest]
|
||||||
: Obj[Key]
|
? Key extends keyof Obj
|
||||||
: Key extends keyof FilterUndefinedAndNull<Obj>
|
? ExtractFromForKey<Obj[Key], Rest, ResultType>
|
||||||
? FilterUndefinedAndNull<Obj>[Key] | undefined
|
: Key extends `${infer Key}?`
|
||||||
: undefined
|
? Key extends keyof Obj
|
||||||
|
? ExtractFromForKey<Obj[Key], Rest, ResultType | undefined>
|
||||||
|
: never
|
||||||
|
: never
|
||||||
|
: never
|
||||||
|
|
||||||
type HasNoMoreKeys<K> = K extends [] ? true : false
|
export type Get<
|
||||||
|
O,
|
||||||
type GetByKeyPath<Obj, Keys> = If<
|
P extends KeyPaths<O, Options, Filter>,
|
||||||
HasNoMoreKeys<Keys>,
|
Options extends KeyPaths_Options = {},
|
||||||
Obj,
|
Filter = null | undefined
|
||||||
Keys extends [infer Key, ...infer Rest] ? ExtractFromForKey<Obj, Key, Rest> : never
|
> = GetByKeyPath<O, KeyPath<P>>
|
||||||
>
|
|
||||||
|
|
||||||
export type Get<O extends object, P extends KeyPaths<O>> = GetByKeyPath<O, KeyPath<P>>
|
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
|
export type * from './and.js'
|
||||||
export type * from './assign.js'
|
export type * from './assign.js'
|
||||||
export type * from './built-ins.js'
|
export type * from './built-ins.js'
|
||||||
|
export type * from './extends-exactly.js'
|
||||||
|
export type * from './extends.js'
|
||||||
export type * from './get.js'
|
export type * from './get.js'
|
||||||
export type * from './if.js'
|
export type * from './if.js'
|
||||||
export type * from './is-any.js'
|
export type * from './is-any.js'
|
||||||
@@ -14,3 +17,4 @@ export type * from './optional-keys-of.js'
|
|||||||
export type * from './pick-assignable.js'
|
export type * from './pick-assignable.js'
|
||||||
export type * from './primitive.js'
|
export type * from './primitive.js'
|
||||||
export type * from './simplify.js'
|
export type * from './simplify.js'
|
||||||
|
export type * from './xor.js'
|
||||||
|
|||||||
@@ -1,55 +1,101 @@
|
|||||||
import type { If } from './if.js'
|
import type { If } from './if.js'
|
||||||
|
import type { Or } from './or.js'
|
||||||
|
import type { And } from './and.js'
|
||||||
|
import type { Not } from './not.js'
|
||||||
import type { IsEmptyString } from './is-empty-string.js'
|
import type { IsEmptyString } from './is-empty-string.js'
|
||||||
import type { IsUndefined } from './is-undefined.js'
|
import type { Extends } from './extends.js'
|
||||||
import type { Simplify } from './simplify.js'
|
import type { ExtendsExactly } from './extends-exactly.js'
|
||||||
|
import type { Concat } from './concat.js'
|
||||||
import type { Assign } from './assign.js'
|
import type { Assign } from './assign.js'
|
||||||
|
import type { NonContainerType } from './non-container-type.js'
|
||||||
|
|
||||||
interface KeyPaths_Options {
|
export interface KeyPaths_Options {
|
||||||
separator?: string
|
separator?: string
|
||||||
leavesOnly?: boolean
|
leavesOnly?: boolean
|
||||||
invertFilter?: boolean
|
invertFilter?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RequiredOptions = Required<KeyPaths_Options>
|
||||||
|
|
||||||
interface KeyPaths_DefaultOptions {
|
interface KeyPaths_DefaultOptions {
|
||||||
separator: '.'
|
separator: '.'
|
||||||
leavesOnly: true
|
leavesOnly: true
|
||||||
invertFilter: false
|
invertFilter: false
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetPrefixedKey<
|
type ExtendsFilter<
|
||||||
Parent extends string,
|
Obj,
|
||||||
Key extends string,
|
Key extends keyof Obj,
|
||||||
Separator extends string
|
Filter,
|
||||||
> = `${If<IsEmptyString<Parent>, '', `${Parent}${Separator}`>}${Key}`
|
MatchExactly extends boolean = false
|
||||||
|
> = MatchExactly extends false
|
||||||
|
? Extends<Obj[Key], Filter> extends false
|
||||||
|
? false
|
||||||
|
: true
|
||||||
|
: ExtendsExactly<Obj[Key], Filter> extends false
|
||||||
|
? false
|
||||||
|
: true
|
||||||
|
|
||||||
type PrefixIfNot<Cond, Prefix, T> = If<Cond, T, Prefix | T>
|
type IncludeElement<Obj, Key extends keyof Obj, Filter, Options extends RequiredOptions> = Obj extends never
|
||||||
|
? false
|
||||||
|
: Obj[Key] extends never
|
||||||
|
? false
|
||||||
|
: unknown extends Obj[Key]
|
||||||
|
? false
|
||||||
|
: Not<
|
||||||
|
Or<
|
||||||
|
And<
|
||||||
|
ExtendsFilter<Obj, Key, Filter, Options['invertFilter']>,
|
||||||
|
Not<Options['invertFilter']>
|
||||||
|
>,
|
||||||
|
And<
|
||||||
|
And<
|
||||||
|
Not<ExtendsFilter<Obj, Key, Filter, Options['invertFilter']>>,
|
||||||
|
Options['invertFilter']
|
||||||
|
>,
|
||||||
|
Not<Obj[Key] extends object | any[] ? true : false>
|
||||||
|
>
|
||||||
|
>
|
||||||
|
>
|
||||||
|
|
||||||
type KeyPathOf<Obj, Options extends Required<KeyPaths_Options>, Parent extends string, Filter = never> =
|
type AdaptKeyForOptionals<Obj, Key extends keyof Obj> = Key extends number | string
|
||||||
Obj extends Record<PropertyKey, unknown>
|
? undefined extends Obj[Key]
|
||||||
? PrefixIfNot<Options['leavesOnly'], Parent, KeyPathsOfStringKeys<Obj, Options, Filter, Parent>>
|
? Obj[Key] extends undefined
|
||||||
: If<IsUndefined<Obj>, never, Parent>
|
? `${Key}`
|
||||||
|
: `${Key}?`
|
||||||
|
: `${Key}`
|
||||||
|
: never
|
||||||
|
|
||||||
|
type NonUndefined<Obj> = Obj extends undefined ? never : Obj
|
||||||
|
type KeysOf<Obj> = Obj extends undefined ? never : Exclude<keyof Obj, keyof any[]>
|
||||||
|
|
||||||
|
type GetCurrentPrefixedKey<Parent extends string, Key extends string, Options extends RequiredOptions> =
|
||||||
|
| Concat<Parent, Key, Options['separator']>
|
||||||
|
| If<Or<Options['leavesOnly'], IsEmptyString<Parent>>, never, Parent>
|
||||||
|
|
||||||
type KeyPathsOfStringKeys<
|
type KeyPathsOfStringKeys<
|
||||||
Obj extends object,
|
Obj,
|
||||||
Options extends Required<KeyPaths_Options>,
|
Key extends keyof Obj,
|
||||||
|
Options extends RequiredOptions,
|
||||||
Filter,
|
Filter,
|
||||||
Parent extends string = ''
|
Parent extends string = ''
|
||||||
> = {
|
> = Key extends Key
|
||||||
[Key in keyof Obj & string]: Obj[Key] extends Filter
|
? IncludeElement<Obj, Key, Filter, Options> extends true
|
||||||
? If<
|
? Obj[Key] extends NonContainerType
|
||||||
Options['invertFilter'],
|
? GetCurrentPrefixedKey<Parent, AdaptKeyForOptionals<Obj, Key>, Options>
|
||||||
KeyPathOf<Obj[Key], Options, GetPrefixedKey<Parent, Key, Options['separator']>, Filter>,
|
: KeyPathsOfStringKeys<
|
||||||
never
|
NonUndefined<Obj[Key]>,
|
||||||
>
|
KeysOf<Obj[Key]>,
|
||||||
: If<
|
Options,
|
||||||
Options['invertFilter'],
|
Filter,
|
||||||
never,
|
Concat<Parent, AdaptKeyForOptionals<Obj, Key>, Options['separator']>
|
||||||
KeyPathOf<Obj[Key], Options, GetPrefixedKey<Parent, Key, Options['separator']>, Filter>
|
>
|
||||||
>
|
: never
|
||||||
}[keyof Obj & string]
|
: never
|
||||||
|
|
||||||
export type KeyPaths<
|
export type KeyPaths<
|
||||||
Obj extends object,
|
Obj,
|
||||||
Options extends KeyPaths_Options = {},
|
Options extends KeyPaths_Options = {},
|
||||||
Filter = null | undefined
|
Filter = null | undefined
|
||||||
> = Simplify<KeyPathsOfStringKeys<Obj, Assign<KeyPaths_Options, KeyPaths_DefaultOptions, Options>, Filter>>
|
> = KeyPathsOfStringKeys<Obj, keyof Obj, Assign<KeyPaths_Options, KeyPaths_DefaultOptions, Options>, Filter> &
|
||||||
|
string
|
||||||
|
|||||||
1
packages/typescript-types/src/not.ts
Normal file
1
packages/typescript-types/src/not.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export type Not<A extends boolean> = A extends true ? false : true
|
||||||
1
packages/typescript-types/src/or.ts
Normal file
1
packages/typescript-types/src/or.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export type Or<A extends boolean, B extends boolean> = A extends true ? true : B extends true ? true : false
|
||||||
1
packages/typescript-types/src/xor.ts
Normal file
1
packages/typescript-types/src/xor.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export type Xor<A extends boolean, B extends Boolean> = [A] extends [B] ? false : true
|
||||||
7
packages/typescript-types/test/and.tst.ts
Normal file
7
packages/typescript-types/test/and.tst.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import type { And } from '@/and.js'
|
||||||
|
import { expect } from 'tstyche'
|
||||||
|
|
||||||
|
expect<And<true, true>>().type.toBe<true>()
|
||||||
|
expect<And<true, false>>().type.toBe<false>()
|
||||||
|
expect<And<false, true>>().type.toBe<false>()
|
||||||
|
expect<And<false, false>>().type.toBe<false>()
|
||||||
8
packages/typescript-types/test/concat.tst.ts
Normal file
8
packages/typescript-types/test/concat.tst.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import type { Concat } from '@/concat.js'
|
||||||
|
import { expect } from 'tstyche'
|
||||||
|
|
||||||
|
expect<Concat<'', '', ''>>().type.toBe<''>()
|
||||||
|
expect<Concat<'Parent', '', ''>>().type.toBe<'Parent'>()
|
||||||
|
expect<Concat<'Parent', 'Child', ''>>().type.toBe<'ParentChild'>()
|
||||||
|
expect<Concat<'Parent', 'Child', '.'>>().type.toBe<'Parent.Child'>()
|
||||||
|
expect<Concat<'', 'Child', '.'>>().type.toBe<'Child'>()
|
||||||
7
packages/typescript-types/test/extends-exactly.tst.ts
Normal file
7
packages/typescript-types/test/extends-exactly.tst.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import type { ExtendsExactly } from '@/extends-exactly.js'
|
||||||
|
import { expect } from 'tstyche'
|
||||||
|
|
||||||
|
expect<ExtendsExactly<true, true>>().type.toBe<true>()
|
||||||
|
expect<ExtendsExactly<true, boolean>>().type.toBe<true>()
|
||||||
|
expect<ExtendsExactly<boolean, true>>().type.toBe<false>()
|
||||||
|
expect<ExtendsExactly<false, true>>().type.toBe<false>()
|
||||||
10
packages/typescript-types/test/extends.tst.ts
Normal file
10
packages/typescript-types/test/extends.tst.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import type { Extends } from '@/extends.js'
|
||||||
|
import { expect } from 'tstyche'
|
||||||
|
|
||||||
|
expect<Extends<true, true>>().type.toBe<true>()
|
||||||
|
expect<Extends<true, boolean>>().type.toBe<true>()
|
||||||
|
expect<Extends<boolean, true>>().type.toBe<boolean>()
|
||||||
|
expect<Extends<false, true>>().type.toBe<false>()
|
||||||
|
expect<Extends<false, never>>().type.toBe<false>()
|
||||||
|
expect<Extends<null, never>>().type.toBe<false>()
|
||||||
|
expect<Extends<undefined, never>>().type.toBe<false>()
|
||||||
@@ -1,19 +1,29 @@
|
|||||||
import type { Get } from '@/get.js'
|
import type { Get } from '@/get.js'
|
||||||
import { expect } from 'tstyche'
|
import { expect } from 'tstyche'
|
||||||
|
|
||||||
interface Example1 {
|
interface ExampleObject {
|
||||||
requiredKey: number
|
nullvalue: null
|
||||||
optionalKey?: number
|
simplevalue: string
|
||||||
unknownNonOptionalKey: unknown
|
unknownKey: unknown
|
||||||
undefinedNonOptionalKey: undefined
|
|
||||||
neverKey: never
|
neverKey: never
|
||||||
subkey: {
|
undefinedKey: undefined
|
||||||
|
undefinedOptionalKey?: undefined
|
||||||
|
optionalKey?: string
|
||||||
|
objectKey: {
|
||||||
name: string
|
name: string
|
||||||
age: number
|
age?: number
|
||||||
}
|
}
|
||||||
|
optionalObjectKey?: {
|
||||||
|
name: string
|
||||||
|
age?: number
|
||||||
|
}
|
||||||
|
arrayKey: [{ name: string; age: number }, 1]
|
||||||
|
optionalArrayKey?: [{ name: string }, { age?: number }]
|
||||||
}
|
}
|
||||||
|
|
||||||
expect<Get<Example1, 'requiredKey'>>().type.toBe<number>()
|
// type T = any extends never ? true : false
|
||||||
expect<Get<Example1, 'optionalKey'>>().type.toBe<number | undefined>()
|
// type A = Get<ExampleObject, 'simplevalue'>
|
||||||
expect<Get<Example1, 'subkey.age'>>().type.toBe<number>()
|
expect<Get<ExampleObject, 'simplevalue'>>().type.toBe<string>()
|
||||||
expect<Get<Example1, 'unknownNonOptionalKey'>>().type.toBe<unknown>()
|
expect<Get<ExampleObject, 'optionalObjectKey?.name', {}, never>>().type.toBe<string | undefined>()
|
||||||
|
expect<Get<ExampleObject, 'optionalObjectKey?.age?', {}, never>>().type.toBe<number | undefined>()
|
||||||
|
expect<Get<ExampleObject, 'optionalArrayKey?.0.name', {}, never>>().type.toBe<string | undefined>()
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import { expect } from 'tstyche'
|
|||||||
expect<KeyPath<''>>().type.toBe<[]>()
|
expect<KeyPath<''>>().type.toBe<[]>()
|
||||||
expect<KeyPath<'Key'>>().type.toBe<['Key']>()
|
expect<KeyPath<'Key'>>().type.toBe<['Key']>()
|
||||||
expect<KeyPath<'Key.Path'>>().type.toBe<['Key', 'Path']>()
|
expect<KeyPath<'Key.Path'>>().type.toBe<['Key', 'Path']>()
|
||||||
|
expect<KeyPath<'Key?.Path'>>().type.toBe<['Key?', 'Path']>()
|
||||||
|
expect<KeyPath<'Key?.Path?'>>().type.toBe<['Key?', 'Path?']>()
|
||||||
expect<KeyPath<'Key.Path', '-'>>().type.toBe<['Key.Path']>()
|
expect<KeyPath<'Key.Path', '-'>>().type.toBe<['Key.Path']>()
|
||||||
expect<KeyPath<'Key.Path-One', '-'>>().type.toBe<['Key.Path', 'One']>()
|
expect<KeyPath<'Key.Path-One', '-'>>().type.toBe<['Key.Path', 'One']>()
|
||||||
|
|
||||||
|
|||||||
@@ -4,50 +4,83 @@ import { expect } from 'tstyche'
|
|||||||
interface ExampleObject {
|
interface ExampleObject {
|
||||||
nullvalue: null
|
nullvalue: null
|
||||||
simplevalue: string
|
simplevalue: string
|
||||||
optionalKey?: {
|
|
||||||
nullvalue: null
|
|
||||||
simplevalue: string
|
|
||||||
}
|
|
||||||
unknownKey: unknown
|
unknownKey: unknown
|
||||||
neverKey: never
|
neverKey: never
|
||||||
config: {
|
undefinedKey: undefined
|
||||||
nullvalue: null
|
undefinedOptionalKey?: undefined
|
||||||
simplevalue: string
|
optionalKey?: string
|
||||||
|
objectKey: {
|
||||||
|
name: string
|
||||||
|
age?: number
|
||||||
}
|
}
|
||||||
|
optionalObjectKey?: {
|
||||||
|
name: string
|
||||||
|
age?: number
|
||||||
|
}
|
||||||
|
arrayKey: [{ name: string; age: number }, 1]
|
||||||
|
optionalArrayKey?: [{ name: string }, { age?: number }]
|
||||||
}
|
}
|
||||||
|
|
||||||
type AllKeys =
|
type KeysWithDefaultSettings =
|
||||||
|
| 'simplevalue'
|
||||||
|
| 'objectKey.name'
|
||||||
|
| 'arrayKey.0.name'
|
||||||
|
| 'arrayKey.0.age'
|
||||||
|
| 'arrayKey.1'
|
||||||
|
|
||||||
|
type KeysWithInvertFilterSettings =
|
||||||
|
| 'nullvalue'
|
||||||
|
| 'undefinedKey'
|
||||||
|
| 'undefinedOptionalKey'
|
||||||
|
| 'optionalKey?'
|
||||||
|
| 'objectKey.age?'
|
||||||
|
| 'optionalObjectKey?.age?'
|
||||||
|
| 'optionalArrayKey?.1.age?'
|
||||||
|
|
||||||
|
type KeysWithSpecialSeparator =
|
||||||
|
| 'simplevalue'
|
||||||
|
| 'objectKey-name'
|
||||||
|
| 'arrayKey-0-name'
|
||||||
|
| 'arrayKey-0-age'
|
||||||
|
| 'arrayKey-1'
|
||||||
|
|
||||||
|
type KeysWhenFilterIsSetToNever =
|
||||||
| 'nullvalue'
|
| 'nullvalue'
|
||||||
| 'simplevalue'
|
| 'simplevalue'
|
||||||
| 'optionalKey.nullvalue'
|
| 'undefinedKey'
|
||||||
| 'optionalKey.simplevalue'
|
| 'undefinedOptionalKey'
|
||||||
| 'unknownKey'
|
| 'optionalKey?'
|
||||||
| 'config.nullvalue'
|
| 'objectKey.name'
|
||||||
| 'config.simplevalue'
|
| 'objectKey.age?'
|
||||||
|
| 'optionalObjectKey?.name'
|
||||||
|
| 'optionalObjectKey?.age?'
|
||||||
|
| 'arrayKey.0.name'
|
||||||
|
| 'arrayKey.0.age'
|
||||||
|
| 'arrayKey.1'
|
||||||
|
| 'optionalArrayKey?.0.name'
|
||||||
|
| 'optionalArrayKey?.1.age?'
|
||||||
|
|
||||||
expect<KeyPaths<ExampleObject>>().type.toBe<
|
type KeysWithLeavesOnlyDisabled =
|
||||||
'simplevalue' | 'optionalKey.simplevalue' | 'unknownKey' | 'config.simplevalue'
|
| 'simplevalue'
|
||||||
>()
|
| 'objectKey'
|
||||||
expect<KeyPaths<ExampleObject, {}, any>>().type.toBeAssignableTo<never>()
|
| 'objectKey.name'
|
||||||
expect<KeyPaths<ExampleObject, {}, unknown>>().type.toBeAssignableTo<never>()
|
| 'arrayKey'
|
||||||
expect<KeyPaths<ExampleObject, {}, never>>().type.toBe<AllKeys>()
|
| 'arrayKey.0'
|
||||||
expect<KeyPaths<ExampleObject, { invertFilter: true }, any>>().type.toBe<AllKeys>()
|
| 'arrayKey.0.name'
|
||||||
expect<KeyPaths<ExampleObject, {}, null>>().type.toBe<
|
| 'arrayKey.0.age'
|
||||||
'simplevalue' | 'optionalKey.simplevalue' | 'unknownKey' | 'config.simplevalue'
|
| 'arrayKey.1'
|
||||||
>()
|
|
||||||
expect<KeyPaths<ExampleObject, {}, string>>().type.toBe<
|
|
||||||
'nullvalue' | 'optionalKey.nullvalue' | 'unknownKey' | 'config.nullvalue'
|
|
||||||
>()
|
|
||||||
expect<KeyPaths<ExampleObject, { separator: '-' }>>().type.toBe<
|
|
||||||
'simplevalue' | 'optionalKey-simplevalue' | 'unknownKey' | 'config-simplevalue'
|
|
||||||
>()
|
|
||||||
expect<KeyPaths<ExampleObject, { leavesOnly: false }>>().type.toBe<
|
|
||||||
'simplevalue' | 'optionalKey' | 'optionalKey.simplevalue' | 'unknownKey' | 'config' | 'config.simplevalue'
|
|
||||||
>()
|
|
||||||
|
|
||||||
expect<KeyPaths<any, {}, any>>().type.toBeAssignableTo<never>()
|
type KeysWithStringValuesOnly = 'simplevalue' | 'objectKey.name' | 'arrayKey.0.name'
|
||||||
expect<KeyPaths<never, {}, any>>().type.toBeAssignableTo<never>()
|
|
||||||
expect<KeyPaths<never, {}, never>>().type.toBeAssignableTo<never>()
|
|
||||||
|
|
||||||
expect<KeyPaths<any, {}, never>>().type.toBe<unknown>()
|
// type A = KeyPaths<ExampleObject, { invertFilter: true }, string>
|
||||||
expect<KeyPaths<any, {}, string>>().type.toBeAssignableTo<unknown>()
|
expect<KeyPaths<ExampleObject>>().type.toBe<KeysWithDefaultSettings>()
|
||||||
|
expect<KeyPaths<ExampleObject, { invertFilter: true }>>().type.toBe<KeysWithInvertFilterSettings>()
|
||||||
|
expect<KeyPaths<ExampleObject, { separator: '-' }>>().type.toBe<KeysWithSpecialSeparator>()
|
||||||
|
expect<KeyPaths<ExampleObject, { leavesOnly: false }>>().type.toBe<KeysWithLeavesOnlyDisabled>()
|
||||||
|
|
||||||
|
expect<KeyPaths<ExampleObject, {}, never>>().type.toBe<KeysWhenFilterIsSetToNever>()
|
||||||
|
expect<KeyPaths<ExampleObject, {}, any>>().type.toBeAssignableWith<never>()
|
||||||
|
|
||||||
|
expect<KeyPaths<ExampleObject, { invertFilter: true }, never>>().type.toBeAssignableWith<never>()
|
||||||
|
expect<KeyPaths<ExampleObject, { invertFilter: true }, any>>().type.toBe<KeysWhenFilterIsSetToNever>()
|
||||||
|
expect<KeyPaths<ExampleObject, { invertFilter: true }, string>>().type.toBe<KeysWithStringValuesOnly>()
|
||||||
|
|||||||
5
packages/typescript-types/test/not.tst.ts
Normal file
5
packages/typescript-types/test/not.tst.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import type { Not } from '@/not.js'
|
||||||
|
import { expect } from 'tstyche'
|
||||||
|
|
||||||
|
expect<Not<true>>().type.toBe<false>()
|
||||||
|
expect<Not<false>>().type.toBe<true>()
|
||||||
7
packages/typescript-types/test/or.tst.ts
Normal file
7
packages/typescript-types/test/or.tst.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import type { Or } from '@/or.js'
|
||||||
|
import { expect } from 'tstyche'
|
||||||
|
|
||||||
|
expect<Or<true, true>>().type.toBe<true>()
|
||||||
|
expect<Or<true, false>>().type.toBe<true>()
|
||||||
|
expect<Or<false, true>>().type.toBe<true>()
|
||||||
|
expect<Or<false, false>>().type.toBe<false>()
|
||||||
7
packages/typescript-types/test/xor.tst.ts
Normal file
7
packages/typescript-types/test/xor.tst.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import type { Xor } from '@/xor.js'
|
||||||
|
import { expect } from 'tstyche'
|
||||||
|
|
||||||
|
expect<Xor<true, true>>().type.toBe<false>()
|
||||||
|
expect<Xor<true, false>>().type.toBe<true>()
|
||||||
|
expect<Xor<false, true>>().type.toBe<true>()
|
||||||
|
expect<Xor<false, false>>().type.toBe<false>()
|
||||||
Reference in New Issue
Block a user