Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How to define typescript interfaces with predefined string literals that restricts allowed types for common attributes?

Idea is that there are two different interfaces called A and B, that has a validation and variant prop, and I am trying to write a function that takes only variant and validation values (field is already defined) to create an Example object.
I keep getting error at below highlighted code line, can someone kindly help to explain what’s going on here? Why ts complains? And any ts documentation that explain this behaviour?

export interface A {
    field: "Af";
    variant: "A";
    validation: boolean;
}

export interface B {
    field: "Af";
    variant: "B";
    validation: number;
}

export type C = A | B;

const c: C = {
    field: "Af",
    variant: "A",
    validation: false,
};

class Example {
    private data: C;
    constructor(data: C) {
        this.data = data;
    }
}

const test = (type: Omit<C, "field">): Example => {
    const d: C = { // should not throw error (Why throwing error here)
        field: "Af",
        ...type,
    };
    const example = new Example(d);
    return example;
};

test({ variant: "A", validation: true }); // should not throw error
test({ variant: "B", validation: 1 }); // should not throw error
test({ variant: "A", validation: 2 }); // should throw error
test({ variant: "B", validation: true }); // should throw error

Error I am getting is

Type '{ variant: "A" | "B"; validation: number | boolean; field: "Af"; }' is not assignable to type 'C'.
  Type '{ variant: "A" | "B"; validation: number | boolean; field: "Af"; }' is not assignable to type 'B'.
    Types of property 'variant' are incompatible.
      Type '"A" | "B"' is not assignable to type '"B"'.
        Type '"A"' is not assignable to type '"B"'.ts(2322)

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>Solution :

The version of Omit in the base libraries is not distributive. So the resulting type will be an object type that mangles the union ({ variant: "A" | "B"; validation: number | boolean; field: "Af"; })

You ca easily write a distributive version of Omit though using distributive conditional types:

type DistributiveOmit<T, K extends PropertyKey> = 
    T extends T ? Omit<T, K> : never;

const test = (type: DistributiveOmit<C, "field">): Example => {
    const d: C = { // ok
        field: "Af",
        ...type,
    };
    //...
};

Playground Link

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading