How to make a TypeScript Interface optional for testing

02/19/2019

While I working with some TypeScript, I ran into a little dilemma. I wanted to generate random model objects for testing while enforcing only the correct interface properties. I did some Googling, like you may be now, and found a GitHub gist with an interesting tid bit.

type Optional<T> = { [P in keyof T]?: T[P] }

This little nugget was exactly what I was looking for! This type allows you to turn any interface into having all optional fields. So I did a small refactor. Note the use of any before and after.

Before

export const randomPet = (pet?: any): IPet => ({
    id: chance.guid(),
    name: chance.name(),
    species: chance.word(),
    age: chance.age().toString(),
    ...pet,
});

const expectedPet = randomPet({foo: "bar"}); //legal and wrong

After

export const randomPet = (optionalPet?: Optional<IPet>): IPet => ({
    id: chance.guid(),
    name: chance.name(),
    species: chance.word(),
    age: chance.age().toString(),
    ...optionalPet,
});

const expectedPet = randomPet({foo: "bar"}); //TS Error
const expectedPet = randomPet({name: "Kevin"}); //legal and correct

Now when I want to generate a random IPet, TypeScript will remind me that I can only use fields that exist on the IPet interface, yet, allow me to pass any override value that matches the interface.