Compiling enterprise

Ivan Koshelev blog on software development

Articles for tags 'type mapping'

Advanced uses of TypeScript type system 01

From mapped types to compile-time type construction

This article continues exploration of typescript type system applications. Previous articles on pragmatic uses of ad-hoc typing and mapped types showcased type system usages for application developers. This one will go a bit deeper, and is more useful for library developers. Make sure you fully understand concepts explained previously.

Generic classes in C# and Java can be thought of as class templates, which let us use placeholders instead of certain types. What they don't allow is having generic property names. Typescript does not have such limitation and easily lets us do that:

export type MakeProp<TKey extends string , TType> = {
  [key in TKey]: TType
}

// MakeProp<'foo', number>
type equivalent1 = {
    foo: number;
}
TS playground

This does not look so impressive, after all, we could have written this type by hand just as easily. What we should know, however, is that we have passed they type-key barrier ( not a real term :-) ) - we managed to make type into a key and create an object shape using only other types. This means, we can use our 'MakeProp' type within other generic types in Typescript. With mapped and conditional types, this means we can construct much more complex types with our mappings.

continue reading

Pragmatic uses of TypeScript type system 02

Mapped types

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.

continue reading