类型缩小仅允许特定类型的属性 'keyof T'
type narrowing for only allow attributes 'keyof T' of certain type
正在尝试使 keyof T
成为字符串 return 类型,并且在尝试将属性传递给 tags
时似乎可以正常工作。尽管将它用作索引时,它似乎并没有缩小它的范围,因为它将成为一个字符串。
错误:Type 'T[KeyOfType<T, string>]' is not assignable to type 'string'.
我做错了什么还是打字稿限制?
type KeyOfType<T, U> = { [P in keyof T]: T[P] extends U ? P : never }[keyof T];
interface TagProps<T> {
tags: T[];
tagLabelKey: KeyOfType<T, string>;
}
const tag = [{
id: 1,
name: 'raw',
}]
const tags = <T,>({tags, tagLabelKey}: TagProps<T>) => {
const getLabel = (tag: T): string => {
return tag[tagLabelKey];
}
}
tags({ tags: tag, tagLabelKey: 'name' })
这似乎确实是打字稿的限制。来自 this github issue 上的一位打字稿开发人员:
The problem here is that we don't have the higher-order reasoning to say "If a key came from an operation that filters out keys that aren't strings, then indexing by that key must have produced a string". The FilterProperties type is effectively logically opaque from TS's point of view.
他甚至提出了另一种实现相同语义的方法。转换为您的情况(并假设您要将 getLabel
映射到数组和 return 结果):
interface TagProps<P extends string> {
tags: Record<P, string>[];
tagLabelKey: P;
}
const tags = <P extends string,>({tags, tagLabelKey}: TagProps<P>) => {
const getLabel = (tag: Record<P, string>): string => {
return tag[tagLabelKey];
}
return tags.map(getLabel)
}
const tag = [{
id: 1,
name: 'raw',
}]
tags({ tags: tag, tagLabelKey: 'name' }) // typechecks
tags({ tags: tag, tagLabelKey: 'id' }) // does not typecheck
高级区别在于不是在标签对象类型中通用并提取字符串键(通过KeyOfType
),在键中是通用的,并使用它构建标签对象的相关部分,同时只允许将字符串分配给它(通过Record<P, string>
)。
或者使用更少的显式类型和更多的类型推断:
interface TagProps<P extends string> {
tags: Record<P, string>[];
tagLabelKey: P;
}
const tags = <P extends string,>({tags, tagLabelKey}: TagProps<P>): string[] => {
return tags.map((tag) => tag[tagLabelKey])
}
正在尝试使 keyof T
成为字符串 return 类型,并且在尝试将属性传递给 tags
时似乎可以正常工作。尽管将它用作索引时,它似乎并没有缩小它的范围,因为它将成为一个字符串。
错误:Type 'T[KeyOfType<T, string>]' is not assignable to type 'string'.
我做错了什么还是打字稿限制?
type KeyOfType<T, U> = { [P in keyof T]: T[P] extends U ? P : never }[keyof T];
interface TagProps<T> {
tags: T[];
tagLabelKey: KeyOfType<T, string>;
}
const tag = [{
id: 1,
name: 'raw',
}]
const tags = <T,>({tags, tagLabelKey}: TagProps<T>) => {
const getLabel = (tag: T): string => {
return tag[tagLabelKey];
}
}
tags({ tags: tag, tagLabelKey: 'name' })
这似乎确实是打字稿的限制。来自 this github issue 上的一位打字稿开发人员:
The problem here is that we don't have the higher-order reasoning to say "If a key came from an operation that filters out keys that aren't strings, then indexing by that key must have produced a string". The FilterProperties type is effectively logically opaque from TS's point of view.
他甚至提出了另一种实现相同语义的方法。转换为您的情况(并假设您要将 getLabel
映射到数组和 return 结果):
interface TagProps<P extends string> {
tags: Record<P, string>[];
tagLabelKey: P;
}
const tags = <P extends string,>({tags, tagLabelKey}: TagProps<P>) => {
const getLabel = (tag: Record<P, string>): string => {
return tag[tagLabelKey];
}
return tags.map(getLabel)
}
const tag = [{
id: 1,
name: 'raw',
}]
tags({ tags: tag, tagLabelKey: 'name' }) // typechecks
tags({ tags: tag, tagLabelKey: 'id' }) // does not typecheck
高级区别在于不是在标签对象类型中通用并提取字符串键(通过KeyOfType
),在键中是通用的,并使用它构建标签对象的相关部分,同时只允许将字符串分配给它(通过Record<P, string>
)。
或者使用更少的显式类型和更多的类型推断:
interface TagProps<P extends string> {
tags: Record<P, string>[];
tagLabelKey: P;
}
const tags = <P extends string,>({tags, tagLabelKey}: TagProps<P>): string[] => {
return tags.map((tag) => tag[tagLabelKey])
}