How to write a function that modifies a given property of an object where the property must be a certain type?

huangapple go评论63阅读模式
英文:

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, &#39;seeds&#39;);
double(apple, &#39;size&#39;);

console.log(apple); // =&gt; { 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&lt;T&gt;(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&lt;T extends { [k in K]: number }, K extends keyof T&gt;(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 的自动建议提示。

第二种方法。定义两个通用类型 TK 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 的键集,所以 objectT。这种方法的缺点是要求 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&lt;K, number&gt;:

function double&lt;K extends string&gt;(object: Record&lt;K, number&gt;, property: K) {
  object[property] *= 2;
}

double(apple, &#39;seeds&#39;);
double(apple, &#39;size&#39;);
double(apple, &#39;invalid&#39;); // 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&lt;K, number&gt;:


function double&lt;T, K extends keyof T&gt;(object: Record&lt;K, number&gt;, property: K) {
  object[property] *= 2;
}

double(apple, &#39;seeds&#39;); // autosuggestions working
double(apple, &#39;incorrect&#39;); // 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&lt;K, number&gt;, 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.

playground

huangapple
  • 本文由 发表于 2023年6月15日 06:14:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/76477910.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定