英文:
How to write a function that modifies a given property of an object where the property must be a certain type?
问题
以下是代码部分的翻译:
type Apple = {
seeds: number,
size: number
};
const apple = {
seeds: 5,
size: 2
};
double(apple, 'seeds');
double(apple, 'size');
console.log(apple); // => { seeds: 10, size: 4 }
这是JS中的常见操作:
function double(object, property) {
object[property] *= 2;
}
在TS中,这会变得非常复杂。这个显然不起作用:
function double<T>(object: T, property: keyof T) {
object[property] *= 2;
}
算术运算的左侧必须是类型 'any'、'number'、'bigint' 或枚举类型
我曾对这个工作有很高的期望,但是...没有运气:
function double<T extends { [k in K]: number }, K extends keyof T>(object: T, property: K) {
object[property] *= 2;
}
类型 'number' 无法赋值给类型 'T[K]'。
'number' 可以赋值给 'T[K]' 的约束,但 'T[K]' 可能会实例化为不同的 'number' 子类型。
我认为这在TS强制执行这一点很奇怪,而在非泛型情况下不会这样做,但无论如何。还有更好的想法吗?
英文:
What I want to do:
type Apple = {
seeds: number,
size: number
};
const apple = {
seeds: 5,
size: 2
};
double(apple, 'seeds');
double(apple, 'size');
console.log(apple); // => { seeds: 10, size: 4 }
This is trivial in JS:
function double(object, property) {
object[property] *= 2;
}
In TS it's an absolute nightmare. This obviously doesn't work:
function double<T>(object: T, property: keyof T) {
object[property] *= 2;
}
> The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type
I had high hopes of this working, but... no luck:
function double<T extends { [k in K]: number }, K extends keyof T>(object: T, property: K) {
object[property] *= 2;
}
> Type 'number' is not assignable to type 'T[K]'.
> 'number' is assignable to the constraint of type 'T[K]', but 'T[K]' could be instantiated with a different subtype of constraint 'number'.
I think it's odd that TS should enforce this when it won't do so for non-generics, but so be it. Any better ideas?
答案1
得分: 1
以下是翻译好的内容:
第一种选项。不需要为对象定义通用类型,只需定义 K
并使 object
参数的类型为 Record<K, number>
:
function double<K extends string>(object: Record<K, number>, property: K) {
object[property] *= 2;
}
double(apple, 'seeds');
double(apple, 'size');
double(apple, 'invalid'); // 错误
这种方法的缺点是没有关于 property
的自动建议提示。
第二种方法。定义两个通用类型 T
和 K extends keyof T
,然而,我们不会将 object
类型定义为 T
,而是只定义为 Record<K, number>
:
function double<T, K extends keyof T>(object: Record<K, number>, property: K) {
object[property] *= 2;
}
double(apple, 'seeds'); // 自动建议提示工作
double(apple, 'incorrect'); // 错误
这种方法的优点是现在可以使用自动建议提示。编译器能够推断出 T
的类型,因为我们将 object
类型定义为 Record<K, number>
,并且由于 K
扩展了 T
的键集,所以 object
是 T
。这种方法的缺点是要求 object
必须只有 number
类型的字段,否则编译器会引发错误,因为 K
将成为所有键的联合类型,意味着 T
的所有字段都必须是数字类型。
英文:
There are a couple of options here:
First. No need to define a generic for an object, only define K
and make object
parameter be of type Record<K, number>
:
function double<K extends string>(object: Record<K, number>, property: K) {
object[property] *= 2;
}
double(apple, 'seeds');
double(apple, 'size');
double(apple, 'invalid'); // error
The downside of this approach is that there are no autosuggestions for the property
Second. Define two generics T
and K extends keyof T
, however, we won't type object
as T
but rather just a Record<K, number>
:
function double<T, K extends keyof T>(object: Record<K, number>, property: K) {
object[property] *= 2;
}
double(apple, 'seeds'); // autosuggestions working
double(apple, 'incorrect'); // error
The advantage is that autosuggestions are now working. The compiler is able to infer the type for T
the because we have typed object
as Record<K, number>
, and since K
extends keyof T
, object
is T
. The downside of this method is that object
is required to have only number
fields, otherwise the compiler will raise an error, since K
will be a union of all keys, meaning that all fields of T
are numbers.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论