Search code examples
typescript

Replacing property of a typescript type


I have a type:

type first = {
    one: number;
    two: string;
    three: {
        four: string,
        five: number,
    }
}

It is applicable to one instance of a variable that I declare in one part of my application, but not exactly applicable to another (second) instance of a variable.

The type that would be suitable for second instance of a variable would look like this:

type second = {
    one: number;
    two: string;
    three: {
        four: string,
        five: number[], //difference
    }
}

I don't want to declare a new type from scratch for a small difference and would like to assimilate the existing type first by replacing the type of property three.

I tried to do it this way:

type second = Pick<first, Exclude<keyof first, 'three'>> & { 
    three: {
        four: string,
        five: number[], //difference
    } 
}

But it gives me an error and I get this type definition on hover:

type second = {
    one: number;
    two: string;
    three: {
        four: string,
        five: number,
    };
    three: {
        four: string,
        five: number,
    };
}

Notice 2 properties three.

What am I doing wrong?


Solution

  • You solution should work, I just have two observations:

    1. You can use the same approach one level down using type queries, no need to rewrite the inner object

    2. Careful with intersection types, the assumption that they behave exactly the same as hand crafted type is not always true. See this question. We can get around such issues flattening the type using an additional mapped type.

    With these in mind this is what I would do:

    type first = {
        one: number;
        two: string;
        three: {
            four: string,
            five: number,
        }
    }
    
    type Identity<T> = { [P in keyof T]: T[P] }
    type Replace<T, K extends keyof T, TReplace> = Identity<Pick<T, Exclude<keyof T, K>> & {
        [P in K] : TReplace
    }>
    
    type second = Replace<first, 'three', Replace<first['three'], 'five', number[]>>
    let d: second = {
        one: 1,
        two: "",
        three : {
            four: "",
            five: [0]
        }
    }
    

    Playground link