-
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(xor): support xoring more than two types (up to 200!)
- Loading branch information
Showing
32 changed files
with
319 additions
and
64 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# editorconfig.org | ||
root = true | ||
|
||
[*] | ||
indent_style = space | ||
indent_size = 2 | ||
end_of_line = lf | ||
charset = utf-8 | ||
trim_trailing_whitespace = true | ||
insert_final_newline = true | ||
|
||
[*.md] | ||
trim_trailing_whitespace = false |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,3 +5,4 @@ tsconfig.json | |
.gitignore | ||
.npmrc | ||
.travis.yml | ||
.editorconfig |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
export * from './types/xor.js' | ||
export type * from './types/xor.js' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
/** | ||
* Skip evaluating `U` if `T` is `unknown`. | ||
*/ | ||
export type EvalIfNotUnknown<T, U> = unknown extends T ? never : U; |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
const ts = require('typescript') | ||
|
||
const xorParamCount = 200 | ||
const countOfUniqueLetters = 20 | ||
/** | ||
* Contains ['A', 'B', ..., <countOfUniqueLetters'th_letter_used_in_Array_constructor>] | ||
*/ | ||
const uniqueLetters = [...Array(countOfUniqueLetters).keys()] | ||
.map(i => String.fromCharCode(i + 65)) | ||
const allParamNames = getUniqueSymbolPermutationsGivenPool(uniqueLetters, xorParamCount) | ||
const [,, ...paramNamesExcludingANorB] = allParamNames | ||
|
||
function createXor() { | ||
const modifiers = [ts.factory.createToken(ts.SyntaxKind.ExportKeyword)] | ||
const name = ts.factory.createIdentifier('XOR') | ||
const typeParams = createXorParams() | ||
const type = createXorType() | ||
|
||
return ts.factory.createTypeAliasDeclaration(modifiers, name, typeParams, type) | ||
} | ||
|
||
function createXorParams() { | ||
const xorParams = [ | ||
ts.factory.createTypeParameterDeclaration(undefined, ts.factory.createIdentifier('A')), | ||
ts.factory.createTypeParameterDeclaration(undefined, ts.factory.createIdentifier('B')), | ||
...paramNamesExcludingANorB.map((letter) => ts.factory.createTypeParameterDeclaration( | ||
undefined, | ||
ts.factory.createIdentifier(letter), | ||
undefined, | ||
ts.factory.createTypeReferenceNode('unknown') | ||
)) | ||
] | ||
|
||
return xorParams | ||
} | ||
|
||
function createXorType() { | ||
const unionOfWithouts = ts.factory.createUnionTypeNode([ | ||
createWithoutLettersIntersectingLetter( | ||
allParamNames.filter((letterToExclude) => letterToExclude !== 'A'), | ||
'A', | ||
), | ||
createWithoutLettersIntersectingLetter( | ||
allParamNames.filter((letterToExclude) => letterToExclude !== 'B'), | ||
'B', | ||
), | ||
...paramNamesExcludingANorB.map( | ||
(letter) => ts.factory.createTypeReferenceNode( | ||
'EvalIfNotUnknown', | ||
[ | ||
ts.factory.createTypeReferenceNode(letter), | ||
createWithoutLettersIntersectingLetter( | ||
allParamNames.filter((letterToExclude) => letterToExclude !== letter), | ||
letter, | ||
), | ||
] | ||
) | ||
) | ||
]) | ||
|
||
const type = ts.factory.createTypeReferenceNode('Prettify', [unionOfWithouts]) | ||
|
||
return type | ||
} | ||
|
||
/** | ||
* @param {string[]} lettersExcludingLetter | ||
* @param {string} excludedLetter | ||
*/ | ||
function createWithoutLettersIntersectingLetter(lettersExcludingLetter, excludedLetter) { | ||
const withoutLettersIntersectingLetter = ts.factory.createIntersectionTypeNode([ | ||
createWithout(lettersExcludingLetter, excludedLetter), | ||
ts.factory.createTypeReferenceNode(excludedLetter) | ||
]) | ||
|
||
return withoutLettersIntersectingLetter | ||
} | ||
|
||
/** | ||
* @param {string[]} lettersExcludingLetter | ||
* @param {string} excludedLetter | ||
*/ | ||
function createWithout(lettersExcludingLetter, excludedLetter) { | ||
const type = ts.factory.createTypeReferenceNode('Without', [ | ||
ts.factory.createIntersectionTypeNode( | ||
lettersExcludingLetter.map((letter) => ts.factory.createTypeReferenceNode(letter)) | ||
), | ||
ts.factory.createTypeReferenceNode(excludedLetter) | ||
]) | ||
|
||
return type | ||
} | ||
|
||
/** | ||
* Takes a `symbolPool` and uses them solo and then matches them in pairs until | ||
* the provided count of unique symbols is reached. | ||
* If all possible pairs with the available symbols are already created and the | ||
* `countPermsToGenerate` is still not reached, then triplets will start to be generated, | ||
* then quadruplets, etc. | ||
* | ||
* @example | ||
* ```ts | ||
* getUniqueSymbolPermutationsGivenPool(['A', 'B'], 8) | ||
* // ['A', 'B', 'AA', 'AB', 'BA', 'BB', 'AAA', 'AAB'] | ||
* ``` | ||
* | ||
* @param {string[]} symbolPool | ||
* @param {number} countPermsToGenerate | ||
*/ | ||
function getUniqueSymbolPermutationsGivenPool(symbolPool, countPermsToGenerate) { | ||
const generateItem = (index) => { | ||
if (index < 0) { | ||
return '' | ||
} | ||
const remainder = index % 20 | ||
return generateItem(Math.floor(index / 20) - 1) + symbolPool[remainder] | ||
} | ||
|
||
const result = Array.from({ length: countPermsToGenerate }, (_, i) => generateItem(i)) | ||
|
||
return result | ||
} | ||
|
||
const tempFile = ts.createSourceFile( | ||
'temp.ts', | ||
'', | ||
ts.ScriptTarget.ESNext, | ||
false, ts.ScriptKind.TS, | ||
) | ||
const printer = ts.createPrinter({ | ||
newLine: ts.NewLineKind.LineFeed, | ||
omitTrailingSemicolon: true, | ||
}) | ||
|
||
const xorTsFileContents = ` | ||
import type { EvalIfNotUnknown } from './evalIfNotUnknown.js' | ||
import type { Prettify } from './prettify.js' | ||
import type { Without } from './without.js' | ||
${ | ||
printer.printNode( | ||
ts.EmitHint.Unspecified, | ||
ts.factory.createJSDocComment( | ||
`Restrict using either exclusively the keys of \`T\` or \ | ||
exclusively the keys of \`U\`.\n\n\ | ||
No unique keys of \`T\` can be used simultaneously with \ | ||
any unique keys of \`U\`.\n\n@example\n\ | ||
\`\`\`ts\nconst myVar: XOR<{ data: object }, { error: object }>\n\`\`\`\n\n\ | ||
Supports from 2 up to ${xorParamCount} generic parameters.\n\n\ | ||
More: https://github.com/maninak/ts-xor/tree/master#description\n` | ||
), | ||
tempFile, | ||
) | ||
} | ||
${ | ||
printer.printNode(ts.EmitHint.Unspecified, createXor(), tempFile) | ||
}` | ||
|
||
console.log(xorTsFileContents) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,4 @@ | ||
interface A { | ||
a: string | ||
} | ||
|
||
interface B { | ||
b: string | ||
} | ||
interface A { a: string } | ||
interface B { b: string } | ||
|
||
export type A_OR_B = A | B |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { XOR_A_B_C_D, XOR_A_B_C_D_Nested } from './setup' | ||
|
||
// @ts-expect-error | ||
const test: XOR_A_B_C_D = { a: '', c: '' } | ||
// @ts-expect-error | ||
const testNested: XOR_A_B_C_D_Nested = { a: '', c: '' } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { XOR_A_B_C_D, XOR_A_B_C_D_Nested } from './setup' | ||
|
||
// @ts-expect-error | ||
const test: XOR_A_B_C_D = { a: '', d: '' } | ||
// @ts-expect-error | ||
const testNested: XOR_A_B_C_D_Nested = { a: '', d: '' } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import { XOR_A_B_C_D, XOR_A_B_C_D_Nested } from './setup' | ||
|
||
const test: XOR_A_B_C_D = { a: '' } // OK | ||
const testNested: XOR_A_B_C_D_Nested = { a: '' } // OK |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import { XOR_A_B_C_D, XOR_A_B_C_D_Nested } from './setup' | ||
|
||
const test: XOR_A_B_C_D = { b: '' } // OK | ||
const testNested: XOR_A_B_C_D_Nested = { b: '' } // OK |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { XOR_A_B_C_D, XOR_A_B_C_D_Nested } from './setup' | ||
|
||
// @ts-expect-error | ||
const test: XOR_A_B_C_D = { c: '', d: '' } | ||
// @ts-expect-error | ||
const testNested: XOR_A_B_C_D_Nested = { c: '', d: '' } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import { XOR_A_B_C_D, XOR_A_B_C_D_Nested } from './setup' | ||
|
||
const test: XOR_A_B_C_D = { c: '' } // OK | ||
const testNested: XOR_A_B_C_D_Nested = { c: '' } // OK |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import { XOR_A_B_C_D, XOR_A_B_C_D_Nested } from './setup' | ||
|
||
const test: XOR_A_B_C_D = { d: '' } // OK | ||
const testNested: XOR_A_B_C_D_Nested = { d: '' } // OK |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { XOR_A_B_C_D, XOR_A_B_C_D_Nested } from './setup' | ||
|
||
// @ts-expect-error | ||
const test: XOR_A_B_C_D = {} | ||
// @ts-expect-error | ||
const testNested: XOR_A_B_C_D_Nested = {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import type { XOR } from '../../src' | ||
|
||
interface A { a: string } | ||
interface B { b: string } | ||
interface C { c: string } | ||
interface D { d: string } | ||
|
||
export type XOR_A_B_C_D = XOR<A, B, C, D> | ||
export type XOR_A_B_C_D_Nested = XOR<A, XOR<B, XOR<C,D>>> |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,6 @@ | ||
import { XOR } from './../../src' | ||
import type { XOR } from './../../src' | ||
|
||
interface A { | ||
a: string | ||
} | ||
|
||
interface B { | ||
b: string | ||
} | ||
interface A { a: string } | ||
interface B { b: string } | ||
|
||
export type A_XOR_B = XOR<A, B> |
4 changes: 4 additions & 0 deletions
4
test/two-hundred-xored-types/has-keys-of-first-and-keys-of-last.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import { XOR_200 } from './setup'; | ||
|
||
// @ts-expect-error | ||
const test: XOR_200 = { a: 0, zdt: 0 } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import { XOR_200 } from './setup'; | ||
|
||
const test: XOR_200 = { a: 0 } // OK |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import { XOR_200 } from './setup'; | ||
|
||
// @ts-expect-error | ||
const test: XOR_200 = { zds: 0, zdt: 0 } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import { XOR_200 } from './setup'; | ||
|
||
const test: XOR_200 = { zdt: 0 } // OK |
26 changes: 26 additions & 0 deletions
26
test/two-hundred-xored-types/has-two-hundred-one-params.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import type { XOR } from '../../src' | ||
|
||
// @ts-expect-error | ||
export type XOR_200 = XOR< | ||
{EXTRANEOUS: 0}, | ||
{a: 0}, {b: 0}, {c: 0}, {d: 0}, {e: 0}, {f: 0}, {g: 0}, {h: 0}, {i: 0}, {j: 0}, | ||
{k: 0}, {l: 0}, {m: 0}, {n: 0}, {o: 0}, {p: 0}, {q: 0}, {r: 0}, {s: 0}, {t: 0}, | ||
{aa: 0}, {ab: 0}, {ac: 0}, {ad: 0}, {ae: 0}, {af: 0}, {ag: 0}, {ah: 0}, {ai: 0}, {aj: 0}, | ||
{ak: 0}, {al: 0}, {am: 0}, {an: 0}, {ao: 0}, {ap: 0}, {aq: 0}, {ar: 0}, {as: 0}, {at: 0}, | ||
{ba: 0}, {bb: 0}, {bc: 0}, {bd: 0}, {be: 0}, {bf: 0}, {bg: 0}, {bh: 0}, {bi: 0}, {bj: 0}, | ||
{bk: 0}, {bl: 0}, {bm: 0}, {bn: 0}, {bo: 0}, {bp: 0}, {bq: 0}, {br: 0}, {bs: 0}, {bt: 0}, | ||
{ca: 0}, {cb: 0}, {cc: 0}, {cd: 0}, {ce: 0}, {cf: 0}, {cg: 0}, {ch: 0}, {ci: 0}, {cj: 0}, | ||
{ck: 0}, {cl: 0}, {cm: 0}, {cn: 0}, {co: 0}, {cp: 0}, {cq: 0}, {cr: 0}, {cs: 0}, {ct: 0}, | ||
{da: 0}, {db: 0}, {dc: 0}, {dd: 0}, {de: 0}, {df: 0}, {dg: 0}, {dh: 0}, {di: 0}, {dj: 0}, | ||
{dk: 0}, {dl: 0}, {dm: 0}, {dn: 0}, {do: 0}, {dp: 0}, {dq: 0}, {dr: 0}, {ds: 0}, {dt: 0}, | ||
{za: 0}, {zb: 0}, {zc: 0}, {zd: 0}, {ze: 0}, {zf: 0}, {zg: 0}, {zh: 0}, {zi: 0}, {zj: 0}, | ||
{zk: 0}, {zl: 0}, {zm: 0}, {zn: 0}, {zo: 0}, {zp: 0}, {zq: 0}, {zr: 0}, {zs: 0}, {zt: 0}, | ||
{zaa: 0}, {zab: 0}, {zac: 0}, {zad: 0}, {zae: 0}, {zaf: 0}, {zag: 0}, {zah: 0}, {zai: 0}, {zaj: 0}, | ||
{zak: 0}, {zal: 0}, {zam: 0}, {zan: 0}, {zao: 0}, {zap: 0}, {zaq: 0}, {zar: 0}, {zas: 0}, {zat: 0}, | ||
{zba: 0}, {zbb: 0}, {zbc: 0}, {zbd: 0}, {zbe: 0}, {zbf: 0}, {zbg: 0}, {zbh: 0}, {zbi: 0}, {zbj: 0}, | ||
{zbk: 0}, {zbl: 0}, {zbm: 0}, {zbn: 0}, {zbo: 0}, {zbp: 0}, {zbq: 0}, {zbr: 0}, {zbs: 0}, {zbt: 0}, | ||
{zca: 0}, {zcb: 0}, {zcc: 0}, {zcd: 0}, {zce: 0}, {zcf: 0}, {zcg: 0}, {zch: 0}, {zci: 0}, {zcj: 0}, | ||
{zck: 0}, {zcl: 0}, {zcm: 0}, {zcn: 0}, {zco: 0}, {zcp: 0}, {zcq: 0}, {zcr: 0}, {zcs: 0}, {zct: 0}, | ||
{zda: 0}, {zdb: 0}, {zdc: 0}, {zdd: 0}, {zde: 0}, {zdf: 0}, {zdg: 0}, {zdh: 0}, {zdi: 0}, {zdj: 0}, | ||
{zdk: 0}, {zdl: 0}, {zdm: 0}, {zdn: 0}, {zdo: 0}, {zdp: 0}, {zdq: 0}, {zdr: 0}, {zds: 0}, {zdt: 0} | ||
> |
Oops, something went wrong.