【发布时间】:2020-04-07 19:38:11
【问题描述】:
我不明白为什么 TypeScript 编译器会抱怨这段代码:
import { useState, useEffect } from 'react';
function useSomething<T extends { [key: string]: any }>(): T {
const [state, setState] = useState<T>({}); // Argument of type '{}' is not assignable to parameter of type 'T | (() => T)'.
useEffect(() => {
setState({ hello: 'world' });
});
return state;
}
因为基本上,这段代码有效:const t: { [key: string]: any } = { hello: 'world' }。
编辑:一个更好的例子,问题基本上是,是否可以在 TypeScript 中输入这个?
import { useState } from 'react';
function useComplexState<T extends Record<string, any>>() {
const [state, setState] = useState<T>({});
const setter = (key: keyof T, value: any) => {
setState((state) => ({ ...state, [key]: value }));
return state;
};
const getter = (key: keyof T) => {
return state[key];
}
return { set: setter, get: getter };
}
interface Person {
firstName: string;
lastName: string;
}
const personState = useComplexState<Person>();
personState.get('firstName');
personState.set('lastName', 'Smith');
关于代码的几点说明:
- 我希望
T永远是一个对象{}所以没有人应该能够做到useComplexState<boolean> - 理想情况下,
T不应该是必需的,所以如果我使用useComplexState(),我应该只在里面有一个通用对象Record<string, any>。 - 我希望
state的默认值为{}。
【问题讨论】:
-
因为您可以调用
useSomething<{foo: number}>(),但{}不能分配给{foo: number}。 (注意T extends X并不意味着所有X都可以分配给T。相反)为什么useSomething()还是通用的?你打算怎么称呼它? -
感谢您的评论,但是,我仍然不完全理解这个问题。我知道
{foo: number}不是{},但我认为如果我定义一个具有一组严格属性的接口或类型,就会出现这种情况,但在这里,我的理解是:“作为只要它是一个带有字符串键和任何值的对象,都可以" -
那么为什么这条线会起作用呢?
const t: { [key: string]: any } = {}; -
@jcalz 用更好的用法示例更新了问题。
-
Tis{[k: string]: any}和T extends {[k: string]: any}是有区别的。后者意味着T可能是更严格的类型。所以如果我打电话给useSomething<{foo: number}>()我是说T肯定有foo类型为number的属性。const t: {[k: string]: any} = {}行有效,因为t的类型 是{[k: string]: any}。我不知道如何更清楚地解释这一点:A extends B并不意味着您可以将B类型的值分配给A类型的变量。反之亦然。
标签: typescript typescript-typings