Skip to content

Instantly share code, notes, and snippets.

@tasermonkey
Created October 16, 2023 13:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tasermonkey/10e599845c747c283a6d40e008e66829 to your computer and use it in GitHub Desktop.
Save tasermonkey/10e599845c747c283a6d40e008e66829 to your computer and use it in GitHub Desktop.
Sample typescript code showing some power of the type system
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