试图将 Typescript 函数限制为给定类型的字符串属性
Trying to limit Typescript function to string properties of a given type
基本上我正在尝试编写一个函数,该函数 returns 是给定类型的指定字符串 属性 的排序函数。
sortString<User>('name')
到目前为止我有以下内容:
type KeysAssignableToType<O, T> = {
[K in keyof O]-?: O[K] extends T ? K : never;
}[keyof O];
export const sortString = <T>(field: KeysAssignableToType<T, string | undefined>) => (a: T, b: T): number => {
if (a[field]) {
return b[field] ? a[field].localeCompare(b[field]) : 1
}
return b[field] ? -1 : 0
}
但是存在以下问题。 TS给我一个TS2339: Property 'localeCompare' does not exist on type 'T[KeysAssignableToType ]'
有什么想法吗?
谢谢!
这是目前 TypeScript 的一个限制或缺失的功能。没有类型运算符的行为类似于 KeysAssignableToType<O, T>
,其中编译器可以理解 O[KeysAssignableToType<O, T>]
可分配给 T
当 O
或 T
是 generic. The sort of higher-order logic encoded there just isn't available to the compiler. There's an open feature request at microsoft/TypeScript#48992 时支持这样的类型运算符。直到并且除非它被实施,我们所拥有的只是解决方法。
一个解决方法是移动(或添加)约束,这样我们就可以说 K
被限制为 KeysAssignableToType<O, T>
,而不是(或除了)说 KeysAssignableToType<O, T>
=54=]object 类型 O
被限制为类似 Record<K, T>
的类型。即使 K
和 T
是通用的,编译器也会让您使用键 K
索引到 Record<K, T>
。例如:
type ObjWithKeys<O, T> =
{ [P in KeysAssignableToType<O, T>]?: T };
export const sortString = <T extends ObjWithKeys<T, string | undefined>>(
field: KeysAssignableToType<T, string | undefined>) => (a: T, b: T): number => {
const aField = a[field];
const bField = b[field];
if (aField) {
return bField ? aField.localeCompare(bField) : 1
}
return bField ? -1 : 0
}
之所以有效,是因为我们将 T
限制为等同于 Record<KeysAssignableToType<O, T>, T>
(尽管我添加了 optional modifier 以便可选属性不会出现问题)。因此,当我们使用 field
索引到 a
或 b
时,编译器知道结果将可分配给 string | undefined
.
请注意,我在narrowing them with truthiness checks (side note: you really want undefined
and ""
to be equivalent in sort order? okay I guess). That's due to a bug/limitation mentioned at microsoft/TypeScript#10530; the compiler isn't able to narrow properties if the property index isn't a simple literal type之前将a[field]
和b[field]
复制到它们自己的变量aField
和bField
中,而field
是当然不是简单的类型。通过使用单独的变量,我们可以完全忘记 属性 索引并专注于单个值。
无论如何,现在一切如愿以偿:
interface User {
name: string,
optionalProp?: string,
age: number
}
sortString<User>('name') // ok
sortString<User>('optionalProp') // ok
sortString<User>('age') // error
基本上我正在尝试编写一个函数,该函数 returns 是给定类型的指定字符串 属性 的排序函数。
sortString<User>('name')
到目前为止我有以下内容:
type KeysAssignableToType<O, T> = {
[K in keyof O]-?: O[K] extends T ? K : never;
}[keyof O];
export const sortString = <T>(field: KeysAssignableToType<T, string | undefined>) => (a: T, b: T): number => {
if (a[field]) {
return b[field] ? a[field].localeCompare(b[field]) : 1
}
return b[field] ? -1 : 0
}
但是存在以下问题。 TS给我一个TS2339: Property 'localeCompare' does not exist on type 'T[KeysAssignableToType ]'
有什么想法吗?
谢谢!
这是目前 TypeScript 的一个限制或缺失的功能。没有类型运算符的行为类似于 KeysAssignableToType<O, T>
,其中编译器可以理解 O[KeysAssignableToType<O, T>]
可分配给 T
当 O
或 T
是 generic. The sort of higher-order logic encoded there just isn't available to the compiler. There's an open feature request at microsoft/TypeScript#48992 时支持这样的类型运算符。直到并且除非它被实施,我们所拥有的只是解决方法。
一个解决方法是移动(或添加)约束,这样我们就可以说 K
被限制为 KeysAssignableToType<O, T>
,而不是(或除了)说 KeysAssignableToType<O, T>
=54=]object 类型 O
被限制为类似 Record<K, T>
的类型。即使 K
和 T
是通用的,编译器也会让您使用键 K
索引到 Record<K, T>
。例如:
type ObjWithKeys<O, T> =
{ [P in KeysAssignableToType<O, T>]?: T };
export const sortString = <T extends ObjWithKeys<T, string | undefined>>(
field: KeysAssignableToType<T, string | undefined>) => (a: T, b: T): number => {
const aField = a[field];
const bField = b[field];
if (aField) {
return bField ? aField.localeCompare(bField) : 1
}
return bField ? -1 : 0
}
之所以有效,是因为我们将 T
限制为等同于 Record<KeysAssignableToType<O, T>, T>
(尽管我添加了 optional modifier 以便可选属性不会出现问题)。因此,当我们使用 field
索引到 a
或 b
时,编译器知道结果将可分配给 string | undefined
.
请注意,我在narrowing them with truthiness checks (side note: you really want undefined
and ""
to be equivalent in sort order? okay I guess). That's due to a bug/limitation mentioned at microsoft/TypeScript#10530; the compiler isn't able to narrow properties if the property index isn't a simple literal type之前将a[field]
和b[field]
复制到它们自己的变量aField
和bField
中,而field
是当然不是简单的类型。通过使用单独的变量,我们可以完全忘记 属性 索引并专注于单个值。
无论如何,现在一切如愿以偿:
interface User {
name: string,
optionalProp?: string,
age: number
}
sortString<User>('name') // ok
sortString<User>('optionalProp') // ok
sortString<User>('age') // error