Skip to content

Instantly share code, notes, and snippets.

@chriseppstein
Created January 29, 2018 19:22
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 chriseppstein/421c8c700622058d1794d281dfb14e55 to your computer and use it in GitHub Desktop.
Save chriseppstein/421c8c700622058d1794d281dfb14e55 to your computer and use it in GitHub Desktop.
This is my syntax idea for generating a subset of TypeScript properties for more interesting mapped types.
type Fn<RV, ARGS = any[]> = (...args: ARGS) => RV;
type Maybe<T> = {
// * Mapped types would no longer be limited to a single property map expression. But for each key, only the
// first expression that matches the key would be used.
// * `keyof SOURCE_TYPE extending NARROW_TYPE` is narrowing expression to limit which keys of SOURCE_TYPE
// are enumerated. Only keys having a type that can extend the NARROW_TYPE are enumerated.
// * The syntax for a mapped property type would be extended to allow a generic type declaration, this allows
// the generic to represent the actual concrete type of the key and then be passed along to the type of the
// mapped property.
// * `unmatched keyof` allows the type to define properties based on any keys not enumerated by another property map.
<ARGS>[P in keyof T extending Fn<void, ARGS>]: Fn<void, ARGS>;
<RV, ARGS>[P in keyof T extending Fn<RV, ARGS>]: Fn<Maybe<RV>, ARGS>;
[P in unmatched keyof T]: Maybe<T[P]>;
};
@acutmore
Copy link

With some boilerplate can get close with conditional types

type Diff<T, U> = T extends U ? never : T;  // Remove types from T that are assignable to U

type FunctionPropertyNames<T> = { [K in keyof T]: T[K] extends Function ? K : never }[keyof T];

// Declare one of the rules
type MappedTypes_Map1<T> = {
    [P in FunctionPropertyNames<T>]: T[P];
}

// Use diff to get the keys which were not matched by Map1 above
type MappedTypes_Unmatched<T> = {
    [P in Diff<keyof T, keyof MappedTypes_Map1<T>>]: T[P] | undefined; 
}

// Produce final type by joining the above together
type MappedTypes<T> = MappedTypes_Map1<T> & MappedTypes_Unmatched<T>;

// Example:
declare const m: MappedTypes<{
    prop1: () => void;
    prop2: string;
}>;

type t1 = typeof m['prop1']; // () => void
type t2 = typeof m['prop2']; // string | undefined

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment