【问题标题】:How to use Template Literal Types as a return type property in TypeScript如何在 TypeScript 中使用模板文字类型作为返回类型属性
【发布时间】:2021-01-12 01:46:30
【问题描述】:

我正在尝试TypeScript 4.1 版本中的新Template Literal Types 功能,并希望创建一个接受字符串文字 参数的函数,该参数随后用于声明返回类型对象的属性.

作为一个实验,我正在尝试围绕 React 的 useState 函数创建一个小包装器,但无法让它工作。

这是我尝试过的:

function useNamedState<TValue>(name: string, initialValue: TValue): {
    [typeof name]: TValue;
    [`set${Capitalize<typeof name>}`]: (newValue: TValue) => void;
} {
    const [value, setValue] = useState(initialValue);

    return {
        [name]: value,
        [`set${capitalize(name)}`]: newValue => setValue(newValue),
    };
}

我不确定这里应该使用什么正确的语法。我想实现以下API:

const {apples, setApples} = useNamedState<number|null>('apples', null);

我怎样才能让它工作?

【问题讨论】:

    标签: typescript


    【解决方案1】:

    您需要一个额外的类型来捕获name 参数的实际字符串文字类型。此外,您还需要使用映射类型或预定义的 Record 映射类型来创建带有来自字符串文字类型的键的类型。

    一个复杂的问题是 TS 不支持部分参数推断,因此您可以选择如何定义函数。

    1. 具有显式类型参数或推断参数但具有类型断言的单个函数:
    
    function useNamedState<TValue, TName extends string>(name: TName, initialValue: TValue): Record<TName, TValue> & Record<`set${Capitalize<typeof name>}`, (newValue: TValue) => void>
    { /*...*/ }
    
    const {apples, setApples} = useNamedState<number|null, 'apples'>('apples', null); // explicit args 
    const {bannana, setBannana} = useNamedState('bannana', null as null | number); // implicit args,but with type assertion on value  
    

    Playground Link

    1. 带有函数柯里化。具有相同顺序的参数(但类型参数列表很笨重)
    function useNamedState <TName extends string>(name: TName)
    {
        return function  <TValue>(initialValue: TValue): Record<TName, TValue> & Record<`set${Capitalize<typeof name>}`, (newValue: TValue) => void> {
            // ....
        }
    }
    // TValue specified in the middle is hard to read
    const {apples, setApples} = useNamedState('apples')<number|null>(null);
    

    Playground Link

    1. 带有函数柯里化。以相反的顺序使用参数(但可以输入参数列表)
    function useNamedState <TValue>(initialValue: TValue)
    {
        return function <TName extends string>(name: TName): Record<TName, TValue> & Record<`set${Capitalize<typeof name>}`, (newValue: TValue) => void> {
             //....
        }
    }
    
    const {apples, setApples} = useNamedState<number|null>(null)('apples');
    

    Playground Link

    【讨论】:

    • 很遗憾不支持部分类型参数推断,希望以后会添加。感谢您的完整回答。
    猜你喜欢
    • 2021-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多