Skip to content

Instantly share code, notes, and snippets.

@flut1
Last active March 7, 2024 11:22
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save flut1/15ae3c37adc52cc6a444ca0266d0f096 to your computer and use it in GitHub Desktop.
Save flut1/15ae3c37adc52cc6a444ca0266d0f096 to your computer and use it in GitHub Desktop.
Common TypeScript utility types
/**
* Extracts all keys of object T that have a value assignable to V
*/
type KeysOfType<T, V> = {
[K in keyof T]: T[K] extends V ? K : never
}[keyof T];
namespace KeysOfTypeExample {
type Foo = {
prop1: number,
prop2: string,
};
type NumberKeysOfFoo = KeysOfType<Foo, number>;
// NumberKeysOfFoo = "prop1"
}
/**
* Inverse of KeysOfType
*
* Extracts all keys of object T that have a value NOT assignable to V
*/
type KeysNotOfType<T, V> = {
[K in keyof T]: T[K] extends V ? never : K
}[keyof T];
namespace KeysNotOfTypeExample {
type Foo = {
prop1: number,
prop2: string,
};
type NonNumberKeysOfFoo = KeysNotOfType<Foo, number>;
// NonNumberKeysOfFoo = "prop2"
}
/**
* All keys of T which refer to optional or possibly undefined properties
*/
export type OptionalKeys<T> = {
[K in keyof T]-?: undefined extends T[K] ? K : never;
}[keyof T];
namespace OptionalKeysExample {
type Foo = {
prop1?: number,
prop2: string,
prop3: boolean | undefined,
};
type OptionalKeysOfFoo = OptionalKeys<Foo>;
// OptionalKeysOfFoo = "prop1" | "prop3"
}
/**
* Include only the properties of T that have a value assignable to T
*/
type FilterProperties<T, V> = Pick<T, KeysOfType<T, V>>;
namespace FilterPropertiesExample {
type Foo = {
prop1: number,
prop2: string,
};
type FilteredFoo = FilterProperties<Foo, number>;
// FilteredFoo = {"prop1": number}
}
/**
* Exclude the properties of T that have a value assignable to T
*/
type ExcludeProperties<T, V> = Pick<T, KeysNotOfType<T, V>>;
namespace FilterPropertiesExample {
type Foo = {
prop1: number,
prop2: string,
};
type FilteredFoo = ExcludeProperties<Foo, number>;
// FilteredFoo = {"prop2": string}
}
/**
* One or multiple T
*/
export type ArrayOrSingle<T> = Array<T> | T;
/**
* If the given value is a Promise, will give the resolved type
* Otherwise gives the type as-is
*/
export type Resolved<T> = T extends Promise<infer R> ? R : T;
namespace ResolvedExample {
async function foo() {
return 5;
}
type AsyncFoo = ReturnType<typeof foo>;
// AsyncFoo = Promise<number>
type ResolvedFoo = Resolved<ReturnType<typeof foo>>;
// ResolvedFoo = number
}
type TypedObjectKeys = <TInput>(input: TInput) => Array<keyof TInput>;
namespace TypedObjectKeysExample {
const testInput = { foo: 1, bar: 'two' };
const keys = (Object.keys as TypedObjectKeys)(testInput);
// typeof keys === Array<'foo'|'bar'>
}
/**
* Picks only the properties of T which are optional, and makes them required (not undefined).
* This can be used to type the "default values" object for any options object
*/
export type DefaultsFor<T> = Required<{ [K in OptionalKeys<T>]: Exclude<T[K], undefined> }>;
namespace DefaultsForExample {
type Options = {
option1?: number,
option2: string,
option3: boolean | undefined,
};
// DefaultsFor<Options> === 'options1' | 'option3'
const defaultOptions: DefaultsFor<Options> = {
option1: 0,
option3: false,
}
function someFunction(options: Options) {
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment