Typescript, indexera en gränssnittsnyckel med en annan gränssnitt


Jag har två gränssnitt med identiska valfria nycklar, men olika värden:

interface Obj1 {
  a?: string
  b?: string
  c?: number 
}

interface Obj2 {
  a: boolean
  b: string
  c: number 
}

Obj1 används som ett funktionsargument, den andra, Obj2, är returvärdet av den funktionen. Jag vill att returtypen endast identifierar de angivna nycklarna på Obj1. Så om Obj1 endast innehöll a och b, kommer Obj2 också endast att innehålla a och b.

Jag försökte med tillvägagångssättet nedan, men jag får ett ts-fel Type 'Property' cannot be used to index type 'ValueType'

type Obj1KeysWithObj2Values<KeyType extends Obj1, ValueType extends Obj2> = {
  [Property in keyof KeyType]: ValueType[Property]
}

UPPDATERING: Funktionen och dess anrop

const myFunc = <T extends Obj1>({ a, b, c }: T) => {
  const returnObj: Partial<Obj2> = {}
  if (a) {
    returnObj.a = true
  }
  if (b) {
    returnObj.b = '1'
  }
  if (c) {
    returnObj.c = 20
  }
  return returnObj as Obj1KeysWithObj2Values<T, Obj2>
}

const resultObj = myFunc({ a: 'hello', b: 'hello' })

Om du provar det, ser du på resultObj att du får vad du än skickar till funktionen, så länge det är i gränssnittet Obj1, oavsett Obj2.

Svar #1

Överväg att använda Pick<Type, Keys> som är en del av standardbiblioteket:

interface Obj1 {
 a?: string
 b?: string
 c?: string
}

interface Obj2 {
 a?: boolean
 b?: boolean
 c?: boolean
}

type Result = Pick<Obj2, keyof Obj1>

Det första argumentet representerar källobjektet, det andra argumentet representerar en förening av nycklar som ska väljas

I ditt fall behöver du också göra en snitt av Obj1-nycklar och Obj2-nycklar:

interface Obj1 {
  a?: string
  b?: string
  c?: number
}

interface Obj2 {
  a: boolean
  b: string
  c: number
}

const myFunc = <T extends Obj1>({ a, b, c }: T) => {
  const returnObj: Partial<Obj2> = {}
  if (a) {
    returnObj.a = true
  }
  if (b) {
    returnObj.b = '1'
  }
  if (c) {
    returnObj.c = 20
  }
  return returnObj as Pick<Obj2, keyof T & keyof Obj2>
}

const resultObj = myFunc({ a: 'hello', b: 'hello' })

Lekplats

Svar #2

Problemet är att även om du vet att Obj2 alltid kommer att ha en superset av Obj1's nycklar, så vet inte TypeScript det, så det behöver bli försäkrat. :-) Om du vill använda de generiska typerna kan du rensa det felet genom att använda en villkorlig typ för att testa att egenskapsnyckeln också finns i ValueType:

type Obj1KeysWithObj2Values<KeyType extends Obj1, ValueType extends Obj2> = {
    [Property in keyof KeyType]: Property extends keyof ValueType ? ValueType[Property] : never;
// −−−−−−−−−−−−−−−−−−−−−−−−−−−−−^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
};

Lekplats exempel

Relaterade artiklar