With Typescript, can I get the type of a value in an object in an array, based on the value of another key in the same object?

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

With Typescript, can I get the type of a value in an object in an array, based on the value of another key in the same object?

问题

以下是翻译的代码部分:

我有以下对象
const features = [{
  key: 'some-key',
  fallback: true
}, {
  key: 'another-key',
  fallback: 'a string'
}] as const;

如你所见fallback 值可以是不同的类型

现在我想要一个函数从 API 中检索特征的值如果没有它应该返回 fallback

但我想要在某些情况下有可能用自己的值覆盖 fallback然后我需要 fallback 的类型 'selected' 键相关

现在我以这种方式获取可能的键列表
type FeatureKey = typeof features[number]['key'];

然后我创建一个函数
function getFeature(key: FeatureKey) {
  // 一些 API 调用,否则返回 fallback
  return features.find(feature => feature.key === key)?.fallback;
}

但我想要添加一个参数来覆盖那一刻的 fallback
function getFeature(key: FeatureKey, fallback: [基于给定键的 FALLBACK 的类型]) {
  // 一些 API 调用,否则返回 fallback
  return features.find(feature => feature.key === key)?.fallback;
}

我的问题是是否可以根据函数中给定的键名获取 fallback 值的类型

因此当我使用 `getFeature('some-key', [...])` 我必须提供一个布尔值作为 fallback 参数当我使用 `getFeature('another-key', [...])` 我必须提供一个字符串作为 fallback
英文:

I got the following object:

const features = [{
  key: 'some-key',
  fallback: true
}, {
  key: 'another-key',
  fallback: 'a string'
}] as const;

As you can see, the fallback value can be different types.

Now I want a function to retrieve the value of a feature from an API. If there is none, it should return the fallback.

But I want the possibility to overwrite the fallback at some cases with my own. Then I need the type of the fallback, related to the 'selected' key.

Now, I get the list of possible keys in this way:

type FeatureKey = typeof features[number]['key'];

Then I create a function:

function getFeature(key: FeatureKey) {
  // Some API Call, but otherwise return fallback
  return features.find(feature => feature.key === key)?.fallback;
}

But I want to add an argument to overwrite the fallback for that moment:

function getFeature(key: FeatureKey, fallback: [TYPEOF FALLBACK BASED ON GIVEN KEY]) {
  // Some API Call, but otherwise return fallback
  return features.find(feature => feature.key === key)?.fallback;
}

My question is: is it possible to get the type of the fallback value, based on the given key name in the function?

So when I use getFeature('some-key', [...]), I have to give in a boolean als fallback argument. And when I use getFeature('another-key', [...]), I have to give in a string as fallback.

答案1

得分: 2

首先,您需要定义它们的关系:

```ts
type FeatureMap = {
  [K in typeof features[number] as K["key"]]: K["fallback"]
}

type FeatureKey = keyof FeatureMap;

并使用泛型声明函数:

function getFeature<T extends FeatureKey>(key: T, fallback: FeatureMap[T])

请注意,fallback 参数的类型将与原始对象相同

例如:当 keysome-key 时,fallback 将为 true 而不是 boolean

如果需要使 fallback 的类型更“宽泛”,我建议以相反的方式定义这些类型。首先创建 FeatureMap,然后使用它来创建 features 对象(如果其他地方仍然需要它):

interface FeatureMap {
  "some-key": boolean
  "another-key": string
}

type Feature<T extends FeatureKey = FeatureKey> = T extends T ? {
  key: T,
  fallback: FeatureMap[T]
} : never

const features: Feature[] = [{
  key: 'some-key',
  fallback: true
}, {
  key: 'another-key',
  fallback: 'a string'
}]

这里是关于 T extends T 的一些解释,供参考:https://stackoverflow.com/questions/69167148/why-ts-toolbelt-library-use-o-extends-unknown-expression


<details>
<summary>英文:</summary>

You will need to define their relationship first:

```ts
type FeatureMap = {
  [K in typeof features[number] as K[&quot;key&quot;]]:K[&quot;fallback&quot;]
}

type FeatureKey = keyof FeatureMap;

and declare the function with generic:

function getFeature&lt;T extends FeatureKey&gt;(key: T, fallback: FeatureMap[T])

note that the type of fallback parameter will be the same to the original object
> for example: when the key is some-key, fallback will be true instead of boolean


If the type of fallback need to be more "widen", I will suggest to
defined these types reversely. Create the FeatureMap first, then use it to create features object (if it still needed for other places):

interface FeatureMap {
  &quot;some-key&quot;: boolean
  &quot;another-key&quot;: string
}

type Feature&lt;T extends FeatureKey = FeatureKey&gt; = T extends T ? {
  key: T,
  fallback: FeatureMap[T]
} : never

const features: Feature[] = [{
  key: &#39;some-key&#39;,
  fallback: true
}, {
  key: &#39;another-key&#39;,
  fallback: &#39;a string&#39;
}]

and here is some explains about T extends T just FYI: https://stackoverflow.com/questions/69167148/why-ts-toolbelt-library-use-o-extends-unknown-expression

答案2

得分: 1

你需要先扩展 features 中的字面类型:

// 以后可以添加更多类型
type Widen<T> = T extends string ? string : T extends number ? number : T extends boolean ? boolean : T;

然后根据给定的键扩展回退类型:

function getFeature<K extends FeatureKeys>(key: K, fallback?: Widen<Extract<typeof features[number], { key: K }>["fallback"]>) {
  return features.find(feature => feature.key === key)?.fallback ?? fallback;
}

你也可以使用映射类型(类似 Jerry 的答案)来简化:

function getFeature<K extends FeatureKeys>(key: K, fallback?: Widen<FeatureMap[K]>) {
    return features.find((feature) => feature.key === key)?.fallback ?? fallback;
}

Playground

英文:

You need to widen the literal type in features first:

// you can tack on more types later
type Widen&lt;T&gt; = T extends string ? string : T extends number ? number : T extends boolean ? boolean : T;

then widen the fallback type based on the given key:

function getFeature&lt;K extends FeatureKeys&gt;(key: K, fallback?: Widen&lt;Extract&lt;typeof features[number], { key: K }&gt;[&quot;fallback&quot;]&gt;) {
  return features.find(feature =&gt; feature.key === key)?.fallback ?? fallback;
}

You can also use a mapped type (like Jerry's answer) to simplify:

function getFeature&lt;K extends FeatureKeys&gt;(key: K, fallback?: Widen&lt;FeatureMap[K]&gt;) {
    return features.find((feature) =&gt; feature.key === key)?.fallback ?? fallback;
}

Playground

答案3

得分: 0

以上的答案将解决我的问题,但目前我选择了一个更简单的解决方案。而不是创建一个名为getFeature的函数来获取布尔值和/或字符串,我将创建两个特定于类型的函数。

因此,我将创建一个getFeatureToggle函数和一个getFeatureValue函数。然后我将始终知道我应该使用布尔值或字符串。

对于其他程序员来说,也许更容易理解要使用哪个函数以及它的作用。

英文:

The answers above will fix my problem, but for now I've chosen for a more simple solution. Instead of creating one function getFeature to get booleans and / or strings, I will make two functions, specific for the type.

So I will create a getFeatureToggle function and a getFeatureValue. Then I will always know I have to use a boolean or a string.

Also for other programmers it's maybe better to understand which function to use and wat it does.

huangapple
  • 本文由 发表于 2023年3月7日 21:11:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/75662423.html
匿名

发表评论

匿名网友

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

确定