If you keep track of TypeScript innovations, you've probably used a Mapped Type like Partial, and maybe even pressed “Go to definition” to see, how it is defined under the hood.
type Partial<T> = {
[P in keyof T]?: T[P];
}
Sadly, while many people are using predefined Mapped Types, few have adapted writing their own to utilize the full power of Typescript. Even fewer still realize the full extent of their power in types like the following:
// Readonly that is applied recursively
// to properties which are objects, instead of just first level
type ReadonlyDeep<TType> = {
readonly [key in keyof TType]: TType extends Object
? ReadonlyDeep<TType[key]>
: TType[key];
}
let t = {b: 1, c: {d:2}}
let readonly: Readonly<typeof t> = t;
readonly.c.d = 5; // :-( no error
let readonlyDeep: ReadonlyDeep<typeof t> = t;
readonlyDeep.c.d = 5; // :-) error!
// A way to get keoyf only for certain types of keys
type KeyofMethods<TType> = ({
[key in keyof TType]: TType[key] extends Function
? key // notice, use of key instead of TType[key]
: never
})[keyof TType];
class FooBar {
a: number = 0;
c(){ return 0;};
d(){ return 0;};
}
type km = KeyofMethods<FooBar>; //"c"|"d"
// A way to prohibit access to anything that is not a function,
// to enforce method usage in part of code, while having relaxed rules elsewhere
type MethodsOf<TType> = Pick<TType, KeyofMethods<TType>>
const m = <MethodsOf<FooBar>> new FooBar();
let c: number = m.c(); // fine
let a = m.a; //error!
TS playground ↗
Lets explore the practical application and peek into still more fantastic possibilities.