Skip to content

Instantly share code, notes, and snippets.

@AriPerkkio
Last active October 17, 2023 10:01
Show Gist options
  • Save AriPerkkio/8b64d61be614177ae9d04d8f79419a97 to your computer and use it in GitHub Desktop.
Save AriPerkkio/8b64d61be614177ae9d04d8f79419a97 to your computer and use it in GitHub Desktop.
Type definition utils I keep forgetting
// https://www.typescriptlang.org/play?#code/C4TwDgpgBAggNnA0hEBnA8mYBLA9gOwEM4AeAFSggA9gJ8ATVKAbygG1EAuKVYAJ2z4A5gF1uhfCCgBfAHxQAvCwBQUNe0RRBUANYpcAMyhkRAfm5kOIgNzLpt5aEhQAqvjz4yuGHz6EQJKrqbh5k4NDUtAxMvALCADRBaj5+IGHOkXSMPPyCQmwiiuwiyvJKzEnsAMq5wgAy2LR+cFr4ru4E6RBiUACiVADGcACu9BAkIZ3h8VA1cUINTcTymdFQ+BAAbhB8lepQpuwAdCcp-l0zc3mLO8Ql+-vck57evv4k-UOj488Xs7ULRq3OCyGZsE5HM5pab-eY3ZoiWS2aRsX7hQoAMhy8wKDmUAHp8cYILxUI5wrNcABbCBdIoAcnQADlevSoAAfKD0sgAdXQbM53IAEgAlXqshyCJoGQgDaAAWRAAEl8NLZdAKmonBBuFVqbTwrY1EQadxYnkjVp6GaAZa4NheDacTY7MplAMCLxYNxFSq1XKiqxtdxGSz6TMTTquTBw1aQzAAIyx+2O9j0pMzekAJnpIns7s9wCgACFuPAkCgMFgPMQSL7VTsZXKyix1oRTdG2fmC-gvQ1eOgDHqaV0MHwxnwINb2qFXqkSMODZAW2xQ6zM7z+RvReLc7YPb2i-3gIPF6O3Lhxzsnh0XlCF-quivhWL11zN7G13u3YSoAABYBUAAWmoSABmAEDfEvHs+wdE8h0fcJUHlB1UDyG9Z3vM9wmfD9M2ZVkXQJIkAOA0CIHAyC+Ggg9YIHBCRyQtwdHwXAAHd8AANWIYYo1+Od3mw5cilXAjPzwrkADF0BcEVv2I-9AJAqgwIgnZqN2Wijzg09EMgVBmNYjjuJGCAswwzoBICISIGfMSNz5cSdzfelpNk+Tf1I5TVKomjCygY9dMY-S6joIRgAACyZXBgHlQhgAGCL0JnSysL02yRK-Byt3fZynNfeSgA
type AllKeysOptional<T extends { [K: string]: any }> = {
[K in keyof T]?: T[K];
};
type UnionToArray<
UnionType extends string,
ArrayType extends string[] = []
> = {
[StringLiteral in UnionType]: Exclude<UnionType, StringLiteral> extends never
? [...ArrayType, StringLiteral]
: UnionToArray<Exclude<UnionType, StringLiteral>, [...ArrayType, StringLiteral]>;
}[UnionType] & string[];
// Tests
type SomeType = 'ONE' | 'TWO' | 'THREE';
interface MyInterface {
type: SomeType;
name: string;
id: string;
list: string[];
}
const A: MyInterface = { type: 'ONE', name: 'A', id: 'A1', list: ['1', '2']};
const B: AllKeysOptional<MyInterface> = { name: 'A' };
const ListOfSomeTypesOrdered: UnionToArray<SomeType> = ['ONE', 'TWO', 'THREE'];
const ListOfSomeTypesUnorder: UnionToArray<SomeType> = ['THREE', 'TWO', 'ONE'];
// @ts-expect-error
const ListOfSomeTypesMissing: UnionToArray<SomeType> = ['TWO', 'ONE'];
// @ts-expect-error
const ListOfSomeTypesUnknownValue: UnionToArray<SomeType> = ['ONE', 'TWO', 'FOUR'];
// @ts-expect-error
const ListOfSomeTypesUnknownValue2: UnionToArray<SomeType> = ['ONE', 'TWO', 'THREE', 'FOUR'];
// @ts-expect-error
const ListOfSomeTypesLengthNotMatching: UnionToArray<SomeType> = ['ONE', 'TWO', 'THREE', 'THREE'];
/*
* Nominal types, a.k.a. opaque types, branded types, tagged types
*/
// This is set in module scope and not exported
// so that developers cannot access user.id[UserID]
declare const UserID: unique symbol;
interface User {
name: string;
id: string & { readonly [UserID]: never };
someOtherId: string;
}
function getUsers(): User[] {
return [];
}
function getUserDetails(id: User['id']) {
return fetch(`/users/${id}`);
}
const users = getUsers();
// OK
users.forEach(user => getUserDetails(user.id));
// @ts-expect-error
users.forEach(user => getUserDetails(user.someOtherId));
// This works here in demo but won't work outside module scope
users[0].id[UserID];
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment