Home > Blockchain >  Typescript: Cloning existing interface and converting its properties to 'string' type
Typescript: Cloning existing interface and converting its properties to 'string' type

Time:09-30

I have the following interfaces, lets say:

interface A {
  innerA: InnerA;
  innerB: InnerB;
}

interface InnerA {
  items: Item[];
  name: string;
  counter: number;
}

interface InnerB {
  address: string;
  code: number;
}

interface Item {
  id: number;
}

What I'm trying to achieve is to create a type or an interface which will have the same keys (including keys of nested objects) as interface 'A', but each of these keys should have a value of type 'string' or, in case it is an object, recursively take its keys and make values of type 'string'. If a key has corresponding value of array type (like InnerA.items), the value of 'items' key should be of type 'Item'.

The following object should describe what I want to get, but I can't figure out a proper type for this:

const labels: CloneType<A> = {
  innerA: { 
    items: { id: 'ID' },
    name: 'Some Name',
    counter: 'Some Counter'
  },
  innerB: {
    address: 'Address',
    code: 'Postal Code'
  }
}

const postalCodeLabel: string = labels.innerB.code;
const itemIdLabel: string = labels.innerA.items.id;

I've tried the following, but stuck on converting array types and also struggling to make it recursive:

type CloneType<Model> = {
  // take keys of generic type
  [modelKey in keyof Model]: {
    // take fields of nested types
    [field in keyof Model[modelKey]]: string;
  }
}

To be honest I doubt it is possible to achieve what I'm trying to do using just pure types, but who knows, maybe it is possible and someone can help.

CodePudding user response:

Check if it's an array, and if it is, we clone the element type, otherwise, we clone it if it's an object, and if it's not any of them, give back a string. Note that this will break for things like Map and Set because those count as objects, so you'll end up with things like { has: string; add: string; ... } for them.

type CloneType<T> = {
    [K in keyof T]:
        T[K] extends (infer E)[]
            ? CloneType<E>
            : T[K] extends object
                ? CloneType<T[K]>
                : string;
}

Assuming that your input is simple, it should work.

Playground

  • Related