typescript 键入脚本,使用另一个接口索引一个接口键

vddsk6oq  于 2023-01-14  发布在  TypeScript
关注(0)|答案(4)|浏览(130)

我有两个具有相同可选键但值不同的接口:

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

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

Obj1是一个函数参数,另一个Obj2是这个函数的返回值,我希望返回值只标识Obj1上的给定键,所以如果Obj1只包含ab,那么Obj2也只包含ab
我尝试使用下面的方法,但得到ts错误Type 'Property' cannot be used to index type 'ValueType'

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

**UPDATE:**函数及其调用

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' })

如果你尝试一下,你会发现在resultObj上你得到了你传递给函数的任何东西,只要它在接口Obj1中,不管Obj2是什么。

nqwrtyyt

nqwrtyyt1#

考虑使用Pick〈Type,Keys〉,它是标准库的一部分:

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

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

type Result = Pick<Obj2, keyof Obj1>

第一个参数表示源对象,第二个参数表示应选取的键的并集
在您的情况下,还需要使Obj1关键帧和Obj2关键帧相交:

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' })

Playground

8fsztsew

8fsztsew2#

问题是,尽管知道Obj2总是有一个Obj1的键的超集,但TypeScript并不知道,因此需要确认。:-)如果您想使用这些泛型,可以通过使用条件类型测试属性键是否也存在于ValueType中来清除该错误:

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

Playground示例

q1qsirdb

q1qsirdb3#

这个answer完全是generic,它工作。
我只是想分享一些类似的东西,我用IntlDate转换成一个自定义的DateTimeFormatParts的日期,除了它是部分通用的。
事情是这样的:

// Accepts T which has any or all Intl.DateTimeFormatOptions properties
export type DateFormatPartsRecord<T extends Intl.DateTimeFormatOptions = Intl.DateTimeFormatOptions> = {
  // modify keys to match props with result props
  [P in keyof T as P extends "fractionalSecondDigits"
    ? "fractionalSecond"
    : P extends "hour12"
    ? "dayPeriod"
    : P]?: string;
};

// argument type for my caller function
export type DateIntlOptions<T extends Intl.DateTimeFormatOptions = Intl.DateTimeFormatOptions> = {
  locale?: string | string[];
  options?: T;
};

// the function which converts formatted intl array parts to my custom object type.
export function ToDateFormatParts<T extends Intl.DateTimeFormatOptions = Intl.DateTimeFormatOptions>(
  val: number | string | Date,
  intl?: DateIntlOptions<T>
): DateFormatPartsRecord<T> {
  const toFormat = new Date(val);
  const reduced: DateFormatPartsRecord<T> = {};
  Intl.DateTimeFormat(intl?.locale, intl?.options ?? { day: "2-digit", month: "short", year: "numeric" })
    .formatToParts(toFormat)
    .forEach(({ type, value }) => {
      if (type === "literal") return;
      reduced[type] = value;
    });
  return reduced;
}

将此应用于您的案例,您可以基于原始类型创建自定义类型:

// instead of obj2 interface:
type Obj2FromObj1<T extends Obj1> = {
  [K extends keyof T]: boolean;
} // or a shorter version: Record<keyof T, boolean>

现在你可以创建一个函数来返回obj1中定义的属性:

function Obj2ValuesFromObj1<T extends Obj1>(args: T): Obj2FromObj1<T> {
  // your handling here.
}
t5zmwmid

t5zmwmid4#

这不是我的代码,虽然它仍然是一个有价值的答案。它是由另一个用户张贴,不久后的答案被删除,所以我不知道谁给予信贷。

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

我对此的解释是,尽管可以随意改进,Obj1KeysWithObj2Values是接受两个参数的泛型类型:

  1. KeyType,它扩展了函数param类型。
  2. ValueType,它扩展了函数返回类型。
    然后声明一个新类型,该类型是一个对象,其键作为KeyType中的键,值类型(如果可以在ValueType上找到)将从那里继承类型或将为never

相关问题