【问题标题】:Return array with values that have types mapped from rest parameter返回具有从 rest 参数映射的类型的值的数组
【发布时间】:2023-03-11 13:13:01
【问题描述】:

我想创建 React 钩子来获取用户设置。 这是一个简化的例子:

import React from "react";

interface UserSettings
{
    SHOW_TIME_IN_HEADER: boolean;
    GRID_SIZE: number;
}

const USER_SETTINGS: UserSettings = {
    SHOW_TIME_IN_HEADER: true,
    GRID_SIZE: 8,
};

export const useUserSetting = <S extends keyof UserSettings>(...setting: S[]): UserSettings[S][] =>
{
    return setting.map(s => USER_SETTINGS[s]);
};

export const Component = () =>
{
    const [
        showTime, // Should be boolean but it is number | boolean
        gridSize // Should be number but it is number | boolean
    ] = useUserSetting("SHOW_TIME_IN_HEADER", "GRID_SIZE");

    return (
        <div>
            {"..."}
        </div>
    );
};

TS Playground

我希望钩子返回一个数组,其中包含相应类型的设置值。在此示例中,返回值的类型均为 number | boolean,但其中只有一个类型应为 numberboolean

这可能吗?该方法将如何命名?我现在最大的问题是找到这个问题的正确单词以便能够搜索它。

如果有人知道我的问题的答案或只知道描述我问题的词语,请告诉我。谢谢。

【问题讨论】:

    标签: typescript types typescript-generics


    【解决方案1】:

    您希望useUserSetting 的返回类型为mapped tuple type。这意味着setting rest 参数不应该输入为S[],因为这只是一个数组类型,会忘记排序。相反,让泛型类型参数S 本身是一个数组类型,并给setting 类型S。这将允许编译器推断 S 的元组类型。

    这是我推荐的打字方式:

    export const useUserSetting = <S extends Array<keyof UserSettings>>(
        ...setting: S): {
            [I in keyof S]: UserSettings[Extract<S[I], keyof UserSettings>]
        } => {
        return setting.map(s => USER_SETTINGS[s]) as any; // need an assertion here
    };
    

    返回类型映射到 S 元组的数字键 I,并生成 UserSettings[S[I]]。不幸的是,编译器无法验证S[I] 是否是UserSettings 的键(它错误地认为I 可能是"push""pop" 等数组的非数字键之一),所以我使用Extract utility type 来处理它。 Extract&lt;S[I], keyof UserSettings&gt; 的意思是“取S[I] 只返回可分配给keyof UserSettings 的部分”;这本质上是一个空操作,但它确实说服了编译器允许我们继续。

    请注意,编译器也无法验证实现 setting.map(s =&gt; USER_SETTNGS[s] 是否可分配给该映射元组类型。 standard library's typings for Array.prototype.map() 仅返回 Array 而不是元组,因此任何排序信息都会丢失。在上面的实现中,我只是使用type assertionany 并继续。大概您更关心useUserSetting()调用者 的体验,而不是实施者 的体验,所以让我们来看看:

    const [
        showTime, // boolean
        gridSize // number
    ] = useUserSetting("SHOW_TIME_IN_HEADER", "GRID_SIZE");
    
    gridSize.toFixed(); // okay
    

    万岁! useUserSetting() 的返回类型是 [number, boolean],因此 gridSize 已知是 number 并且可以这样处理(例如,gridSize.toFixed() 不会导致编译器错误)。

    Playground link to code

    【讨论】:

    • 是的!非常感谢,这正是我想做的!感谢您的解决方案以及解释和链接,这非常有帮助。
    【解决方案2】:

    据我所知,如果目前不可能的话。 this is the best I can come up with

    【讨论】:

      猜你喜欢
      • 2014-08-13
      • 1970-01-01
      • 2018-06-29
      • 1970-01-01
      • 1970-01-01
      • 2019-12-04
      • 2010-12-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多