import type { StringKeyOf, UnionToIntersection } from 'type-fest'; // Values<{ a: string, b: number }> = string | number type Values = T[keyof T]; // ArrayKeys<[string, string, string]> = 0 | 1 | 2 type ArrayKeys = Exclude; // ArrayToUnion<['foo', 'bar']> = 'foo' | 'bar type ArrayToUnion = T[number]; type NoUndefined = T extends undefined ? never : T; // BoxArrayByName = { 0: { [N]: 1 }, 1: { [N]: 2 } } type DeterministicBoxArrayByName = { [K in ArrayKeys]: Record & (T[K] extends string ? Record<`${N}${Capitalize}`, true> : Record) & Record<`${N}${Capitalize, T[K]>>}`, false> & (K extends keyof A ? A[K] : Record); }; /* * OptionWithDerivedProperties<'aProperty', ['foo', 'bar']> = { aProperty: 'foo'; aPropertyFoo: true; aPropertyBar: false; } | { aProperty: 'bar'; aPropertyFoo: false; aPropertyBar: true; } * Deterministic derived options causes too much union and cannot be used as it is for the entire application. * Can be used selectively */ export type DeterministicOptionWithDerivedProperties = Values< DeterministicBoxArrayByName >; // OptionWithDerivedProperties<'aProperty', ['foo', 'bar']> = { aProperty: 'foo' | 'bar'; aPropertyFoo: boolean; aPropertyBar: boolean; } export type OptionWithDerivedProperties = Record, 'any'> | undefined> & Record<`${N}${Capitalize>}`, boolean> & ('no' extends ArrayToUnion ? Record<`${N}Any`, boolean> : Record); // KeyToArray<{ foo: 1, foo2: 2 }> = [{foo: 1}, {foo2: 2}] // type KeyToArray // KeyToArray<{ foo: ['foo'], foo2: ['foo'] }> = [{foo: { foo: 'foo', fooFoo: true }}, {foo2: { foo2: 'foo', foo2Foo: true }}] type OptionBoxByName> = { [K in StringKeyOf]: OptionWithDerivedProperties; }; // OptionsWithDerivedProperties<{ aProperty: ['foo', 'bar'], oProperty: ['foo', 'bar'] }> = { aProperty: 'foo' | 'bar'; aPropertyFoo: boolean; aPropertyBar: boolean; } & { oProperty: 'foo' | 'bar'; oPropertyFoo: boolean; oPropertyBar: boolean; } export type OptionsWithDerivedProperties> = UnionToIntersection>>; /* some samples to test type DatabaseTypeApplication = DeterministicOptionWithDerivedProperties< 'databaseType', ['sql', 'no', 'cassandra', 'couchbase', 'mongodb', 'neo4j'], [{ foo: 'test ' }, { foo2: 'test ' }] >; const foo: DatabaseTypeApplication; if (foo.databaseTypeCassandra) { foo.foo; } if (foo.databaseTypeSql) { // $ExpectType void foo.databaseType foo.foo; } type DatabaseTypeApplication2 = OptionWithDerivedProperties<'databaseType', ['sql', 'no', 'cassandra', 'couchbase', 'mongodb', 'neo4j']>; const foo2: DatabaseTypeApplication2; if (foo2.databaseTypeCassandra) { foo2.databaseType; foo2. } if (foo2.databaseTypeSql) { // $ExpectType void foo2.databaseType; } const foo3: OptionsWithDerivedProperties<{ databaseType: ['sql', 'no', 'cassandra', 'couchbase', 'mongodb', 'neo4j'], messageBroker: ['no', 'kafka', 'pular'], }>; foo3.databaseTypeSql */