Created
October 16, 2023 13:55
-
-
Save tasermonkey/10e599845c747c283a6d40e008e66829 to your computer and use it in GitHub Desktop.
Sample typescript code showing some power of the type system
This file contains hidden or 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
type PreferenceEI = 'I' | 'E'; | |
type PreferenceSN = 'S' | 'N'; | |
type PreferenceTF = 'T' | 'F'; | |
type PreferenceJP = 'J' | 'P'; | |
type Preference = PreferenceEI | PreferenceSN | PreferenceTF | PreferenceJP; | |
type Name = `${PreferenceEI}${PreferenceSN}${PreferenceTF}${PreferenceJP}`; | |
// type Name = | |
// 'ISTJ' | 'ISFJ' | 'INFJ' | 'INTJ' | | |
// 'ISTP' | 'ISFP' | 'INFP' | 'INTP' | | |
// 'ESTP' | 'ESFP' | 'ENFP' | 'ENTP' | | |
// 'ESTJ' | 'ESFJ' | 'ENFJ' | 'ENTJ'; | |
type Temperment = 'NT' | 'NF' | 'SJ' | 'SP'; | |
type CognitiveFunction = 'Se' | 'Si' | 'Te' | 'Ti' | 'Ne' | 'Ni' | 'Fe' | 'Fi'; | |
type IsAny<T> = 0 extends T & 1 ? true : false | |
type CognitiveFunctionOppositeEI<F> = | |
F extends `${infer prefix}i` | |
? `${prefix}e` | |
: F extends `${infer prefix}e` | |
? `${prefix}i` | |
: IsAny<F> extends true ? any : never; | |
type CognitiveFunctionOppositeSN<F> = | |
F extends `S${infer suffix}` | |
? `N${suffix}` | |
: F extends `N${infer suffix}` | |
? `S${suffix}` | |
: IsAny<F> extends true ? any : never; | |
type CognitiveFunctionOppositeTF<F> = | |
F extends `T${infer suffix}` | |
? `F${suffix}` | |
: F extends `F${infer suffix}` | |
? `T${suffix}` | |
: IsAny<F> extends true ? any : never; | |
type CognitiveFunctionOppositeSNTF<F> = | |
F extends `S${infer suffix}` | `N${infer suffix}` | |
? CognitiveFunctionOppositeSN<F> | |
: F extends `T${infer suffix}` | `F${infer suffix}` | |
? CognitiveFunctionOppositeTF<F> | |
: IsAny<F> extends true ? any : never; | |
type CognitiveFunctionOpposite<F> = CognitiveFunctionOppositeEI<CognitiveFunctionOppositeSNTF<F>>; | |
type CognitiveFunctionStack<A extends CognitiveFunction, B extends CognitiveFunction> = (CognitiveFunctionOpposite<A> extends never ? false : true) & (CognitiveFunctionOpposite<B> extends never ? false : true) extends false ? never : Readonly<[A, B, CognitiveFunctionOpposite<B>, CognitiveFunctionOpposite<A>]>; | |
const MBTIs = { | |
ISTJ: { | |
name: 'ISTJ', | |
temperment: 'SJ', | |
stack: ['Si', 'Te', 'Fi', 'Ne'] satisfies CognitiveFunctionStack<'Si', 'Te'> | |
} as const, | |
ISFJ: { | |
name: 'ISFJ', | |
temperment: 'SJ', | |
stack: ['Si', 'Fe', 'Ti', 'Ne'] satisfies CognitiveFunctionStack<'Si', 'Fe'> | |
} as const, | |
INFJ: { | |
name: 'INFJ', | |
temperment: 'NF', | |
stack: ['Ni', 'Fe', 'Ti', 'Se'] satisfies CognitiveFunctionStack<'Ni', 'Fe'> | |
} as const, | |
INTJ: { | |
name: 'INTJ', | |
temperment: 'NT', | |
stack: ['Ni', 'Te', 'Fi', 'Se'] satisfies CognitiveFunctionStack<'Ni', 'Te'> | |
} as const, | |
ISTP: { | |
name: 'ISTP', | |
temperment: 'SP', | |
stack: ['Ti', 'Se', 'Ni', 'Fe'] satisfies CognitiveFunctionStack<'Ti', 'Se'> | |
} as const, | |
ISFP: { | |
name: 'ISFP', | |
temperment: 'SP', | |
stack: ['Fi', 'Se', 'Ni', 'Te'] satisfies CognitiveFunctionStack<'Fi', 'Se'> | |
} as const, | |
INFP: { | |
name: 'INFP', | |
temperment: 'NF', | |
stack: ['Fi', 'Ne', 'Si', 'Te'] satisfies CognitiveFunctionStack<'Fi', 'Ne'> | |
} as const, | |
INTP: { | |
name: 'INTP', | |
temperment: 'NT', | |
stack: ['Ti', 'Ne', 'Si', 'Fe'] satisfies CognitiveFunctionStack<'Ti', 'Ne'> | |
} as const, | |
ESTP: { | |
name: 'ESTP', | |
temperment: 'SP', | |
stack: ['Se', 'Ti', 'Fe', 'Ni'] satisfies CognitiveFunctionStack<'Se', 'Ti'> | |
} as const, | |
ESFP: { | |
name: 'ESFP', | |
temperment: 'SP', | |
stack: ['Se', 'Fi', 'Te', 'Ni'] satisfies CognitiveFunctionStack<'Se', 'Fi'> | |
} as const, | |
ENFP: { | |
name: 'ENFP', | |
temperment: 'NF', | |
stack: ['Ne', 'Fi', 'Te', 'Si'] satisfies CognitiveFunctionStack<'Ne', 'Fi'> | |
} as const, | |
ENTP: { | |
name: 'ENTP', | |
temperment: 'NT', | |
stack: ['Ne', 'Ti', 'Fe', 'Si'] satisfies CognitiveFunctionStack<'Ne', 'Ti'> | |
} as const, | |
ESTJ: { | |
name: 'ESTJ', | |
temperment: 'SJ', | |
stack: ['Te', 'Si', 'Ne', 'Fi'] satisfies CognitiveFunctionStack<'Te', 'Si'> | |
} as const, | |
ESFJ: { | |
name: 'ESFJ', | |
temperment: 'SJ', | |
stack: ['Fe', 'Si', 'Ne', 'Ti'] satisfies CognitiveFunctionStack<'Fe', 'Si'> | |
} as const, | |
ENFJ: { | |
name: 'ENFJ', | |
temperment: 'NF', | |
stack: ['Fe', 'Ni', 'Se', 'Ti'] satisfies CognitiveFunctionStack<'Fe', 'Ni'> | |
} as const, | |
ENTJ: { | |
name: 'ENTJ', | |
temperment: 'NT', | |
stack: ['Te', 'Ni', 'Se', 'Fi'] satisfies CognitiveFunctionStack<'Te', 'Ni'> | |
} as const | |
}; | |
type MBTIsList = typeof MBTIs; | |
type MBTIsElement = MBTIsList[Name]; | |
type AnyCognitiveFunctionStack = MBTIsElement['stack']; //CognitiveFunctionStack<any, any>; | |
// Note this type depends on the MBTIs const list being const at compile time. (const and readonly) | |
interface MBTI<N extends Name = Name> { | |
// This is the constant string literal Name at the given object type above | |
name: Name; | |
// This is the constant string literal for temperment at the given object type above | |
temperment: MBTIsList[N]['temperment']; | |
// So this stack is the constant readonly array type defined in the const above for the given element @ name | |
stack: MBTIsList[N]['stack']; | |
} | |
function functionStack<N extends Name>(name: N): MBTI<N>['stack'] { | |
return MBTIs[name].stack; | |
} | |
// LOL ... we can filter this so that the type is always the correct types for the temperment. | |
type FilterOnTemperment<T extends MBTIsElement['temperment']> = MBTIsElement & { temperment: T }; | |
function isTemperment<T extends MBTIsElement['temperment']>(t: T) { | |
function isSpecificTemperment(m: MBTIsElement): m is MBTIsElement & { temperment: T } { | |
return m.temperment === t; | |
} | |
return isSpecificTemperment; | |
} | |
function mbtisWithTemperment<T extends Temperment>(temperment: T): FilterOnTemperment<T>[] { | |
const isCorrectTemperment = isTemperment(temperment); | |
return Object.values(MBTIs).filter(isCorrectTemperment); | |
} | |
function preferenceIndex(preference: Preference): 0 | 1 | 2 | 3 { | |
switch (preference) { | |
case 'E': | |
case 'I': | |
return 0; | |
case 'S': | |
case 'N': | |
return 1; | |
case 'T': | |
case 'F': | |
return 2; | |
case 'J': | |
case 'P': | |
return 3; | |
default: | |
const invalidPreference: never = preference; | |
throw Error(invalidPreference); | |
} | |
} | |
function hasPreference(mbti: MBTI, preference: Preference): boolean { | |
const index = preferenceIndex(preference); | |
return mbti.name[index] === preference; | |
} | |
function mbtisWithPreference(...preferences: Preference[]): MBTI[] { | |
return Object.values(MBTIs).filter(t => preferences.every(p => hasPreference(t, p))); | |
} | |
function hasFunction(mbti: MBTI, cognitiveFunctions: CognitiveFunction[], positionsAny: (0 | 1 | 2 | 3)[] = [0, 1, 2, 3]): boolean { | |
return cognitiveFunctions.every(f => | |
positionsAny.some(p => mbti.stack[p] === f)); | |
} | |
function mbtisWithFunctions(...cognitiveFunctions: CognitiveFunction[]): MBTI[] { | |
return Object.values(MBTIs).filter(t => hasFunction(t, cognitiveFunctions)) | |
} | |
function introvertGate(str: Name & `I${string}`) { | |
console.log(str); | |
} | |
function MBTIsIntrovertGate(str: MBTIsElement & { name: Name & `I${string}` }) { | |
console.log(str); | |
} | |
function isIntrovert(str: MBTIsElement): str is MBTIsElement & { name: Name & `I${string}` } { | |
return str.name.startsWith('I'); | |
} | |
function isExtrovert(str: MBTIsElement): str is MBTIsElement & { name: Name & `E${string}` } { | |
return str.name.startsWith('E'); | |
} | |
console.log(introvertGate('INFJ')); | |
// @ts-expect-error | |
console.log(introvertGate('ESTP')); | |
Object.values(MBTIs).filter(isIntrovert).map(x => x.name).forEach(introvertGate); | |
// @ts-expect-error | |
Object.values(MBTIs).filter(isExtrovert).map(x => x.name).forEach(introvertGate); | |
console.log(MBTIsIntrovertGate(MBTIs.INFJ)); | |
// @ts-expect-error | |
console.log(MBTIsIntrovertGate(MBTIs.ESTP)); | |
console.log(mbtisWithFunctions("Fi", 'Ni').map(t => `${t.name} (${t.stack.join(' ')})`)); | |
console.log(mbtisWithFunctions(...MBTIs.ENTJ.stack).map(t => `${t.name} (${t.stack.join(' ')})`)); | |
// This gives of a typed list of only NT temperment. | |
const tempermentedMbti = mbtisWithTemperment('NT'); | |
const assertTemperment: (typeof tempermentedMbti)[number]['temperment'] extends 'NT' ? true : false = true; | |
console.log(tempermentedMbti); | |
type SiTe = CognitiveFunctionStack<'Si', 'Te'>; | |
type TeNi = CognitiveFunctionStack<'Te', 'Ni'>; | |
const foo1: SiTe = functionStack('ISTJ'); | |
// @ts-expect-error | |
const foo2: CognitiveFunctionStack<'Te', 'Ni'> = functionStack('ISTJ'); | |
// change |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment