Search code examples
typescripttypescastingtypeerror

How to assign an object a const type while extend it from some other type or interface in Typescript?


Consider code like this:

const obj = {
  a: {x: 1},
  b: {x: 2},
};

const bx = obj.b.x; // bx is number
const nx = obj.n.x; // error as expected

but now I want to have more certain type info about obj, so i do:

const obj = {
  a: {x: 1},
  b: {x: 2},
} as const;

const bx = obj.b.x; // changed:  bx is 2
const nx = obj.n.x; // same:     error as expected

until now everythink is ok, but now I need to restrict other team members to patch/add values to obj in specific general-shape, so:

type GeneralTypeToFollow = { [k in string]: {x: number} }

const obj: GeneralTypeToFollow = {
  a: {x: 1},
  b: {x: 2},
  
  wrong: 'wrong type', // error as expected
} as const;


const bx = obj.b.x; // changed: bx downgraded to number
const nx = obj.n.x; // changed: nx is number (no error at all)

Restriction now works, but bx and nx has lost correct types.

So the expected behavior is:

const bx = obj.b.x; // bx is 2
const nx = obj.n.x; // error

Solution

  • Your only option is to have a generic function. This function can constrain the passed object to GeneralTypeToFollow and also return the the type of the object literal.

    type GeneralTypeToFollow<N extends number> = { [k in string]: { x: N } }
    
    function makeObj<
      T extends GeneralTypeToFollow<N>,
      N extends number
    >(obj: T): T {
        return obj
    }
    
    const obj = makeObj({
      a: { x: 1 },
      b: { x: 2 },
    });
    
    
    const bx = obj.b.x; // 2
    const nx = obj.n.x; // error
    

    By using the additional generic type N to represent the numbers, we can also get rid of as const.

    Playground