首页 > 解决方案 > 对象中的打字稿类型值

问题描述

试图强类型数据格式化函数。功能很简单,

输入:

const eleOfArray = {
  a: 1,
  b: "string",
  c: [1, 2, 3]
}

输出:

const eleOfArray = {
  a: {
    value: 1,
    component: 1
  },
  b: {
    value: "string",
    component: "string"
  },
  c: {
    value: [1, 2, 3],
    component: [1, 2, 3]
  }
}

功能:

export function tableDataFormatter<T>(d: T) {
	const formatedData: ITableData<T> = {}

	Object.keys(d).forEach((key: string) => {
		// tslint:disable-next-line:ban-ts-ignore
		// @ts-ignore
		formatedData[key] = {
			// tslint:disable-next-line:ban-ts-ignore
			// @ts-ignore
			value: d[key as keyof T],
			component: d[key as keyof T]
		}
	})

	return formatedData
}

界面:

interface ITableData<T> {
	readonly [key: keyof T]: {
		readonly component: React.ReactNode
		readonly value: T[keyof T]
	}
}

我在使用此代码时遇到的问题是,当我使用tableDataFormatter它时,它value总是显示string | number.

用法:

public formatData<IBenefit> (data: ReadonlyArray<IBenefit>): ReadonlyArray<ITableData<IBenefit>> {
		return super.formatData(data).map((d: ITableData<IBenefit>) => ({
			...d,
			stores: {
				...d.stores,
				component: <Addon addonValues={d.stores.value} />
        // d.stores.value should be an Array but it's being shown as ReactText/string
    }
  }))
}

所以我必须抑制错误,因为该功能按预期工作,我明确指定valuereadonly value: T[keyof T]

标签: reactjstypescript

解决方案


您的ITableData<T>界面不是有效的 TypeScript。具体来说,您正在尝试使用限制为的索引签名keyof T,但唯一允许的索引签名类型是stringnumber。相反,您应该考虑使用映射类型而不是接口。它会给你你想要的映射:

type ITableData<T> = {
  readonly [K in keyof T]: {
    readonly component: React.ReactNode
    readonly value: T[K]
  }
}

请注意嵌套value属性是 typeT[K]而不是T[keyof T]T[keyof T]将是所有值类型的联合,T并且从每个键到每个值的映射都会丢失。但这T[K]意味着对于每个键K,嵌套属性与索引value的原始属性的类型相同。这是避免问题的方法。 TKstring | number

然后,tableDataFormatter()我将更改一些注释和断言,如下所示:

// strip off the readonly modifier for the top level properties
type Mutable<T> = { -readonly [K in keyof T]: T[K] };

// explicitly declare that the function returns ITableData<T>
export function tableDataFormatter<T extends Record<keyof T, React.ReactNode>>(
  d: T
): ITableData<T> {

  // assert that the return value will be ITableData<T> but make it
  // mutable so we can assign to it in the function without error
  const formatedData = {} as Mutable<ITableData<T>>;

  // assert that Object.keys(d) returns an array of keyof T types
  // this is not generally safe but is the same as your "as keyof T"
  // assertions and only needs to be done once.
  (Object.keys(d) as Array<keyof T>).forEach(key => {
    formatedData[key] = {
      value: d[key],
      component: d[key]
    }
  })

  return formatedData
}

希望这能按您现在期望的方式工作。祝你好运!


推荐阅读