TypeScript type cast

前端 未结 3 593
孤城傲影
孤城傲影 2021-01-24 12:47

I have an object, and I need to add some extra properties to it in some cases.

interface Item {
    name: string
}

functi         


        
3条回答
  •  心在旅途
    2021-01-24 13:16

    1. Union type approach

    What I think you want to do, is to determine some property relations. As it is wrong to just get different type and cast it as it would be another one. I think your Item type can have some additional properties in some circumstances, and that we should model in the type definition!

    Lets start from proper typing:

    type ItemNoFoo = { name: string };
    type ItemWithFoo = ItemNoFoo & {foo: string}
    type Item = ItemNoFoo | ItemWithFoo;
    
    

    I created Item as a union of ItemNoFoo and ItemWithFoo. Thanks that we can define two different states of our object.

    Now I will create a function, guard function which will check if we are ItemNoFoo or ItemWithFoo states.

    const hasFoo = (item: Item): item is ItemWithFoo => item.name === 'test';
    

    Ok great so we can now ask if Item has property foo or not in scope of one type ( as our type is just a union of two other types ).

    The final code:

    type ItemNoFoo = { name: string };
    type ItemWithFoo = ItemNoFoo & {foo: string}
    type Item = ItemNoFoo | ItemWithFoo;
    
    const hasFoo = (item: Item): item is ItemWithFoo => item.name === 'test';
    
    function addProp(obj: Item) {
        if (hasFoo(obj)) {
            obj.foo = 'hello'; // in if block obj is inferenced as ItemWithFoo!
         }
    }
    
    

    Some more info about this approach you can find here - Sum types


    2. Function as data transformer

    If it happens that our function needs to create a new structure, so is like a - data creator, or data transformer, then we should look on it as in -> out pipe. In types, in is ItemNoFoo and out type is ItemWithFoo | ItemWithFoo ( types definitions in point 1 ).

    function transform(obj: ItemNoFoo): Item {
        if (obj.name === 'test') {
            return {
              ...obj,
              foo: 'hello'
            } // here we create new value with new type
         } else {
           return obj;
         }
    }
    

    As you can see still union type Item is handy as this function returns or unchanged ItemNoFoo type instance or ItemWithFoo.

    Lets check the using of it:

    function f(item: ItemNoFoo) {
      const finalItem = transform(item); // type of finalItem is ItemWithFoo | ItemNoFoo
    } 
    

    If further you want to be sure if you deal with one or another, then handy can be hasFoo guard function (defined in solution 1) which will determine which of these two types the value is.

提交回复
热议问题