为什么TypeScript在将对象文字指定给扩展对象文字的泛型类型时会抱怨?

为什么TypeScript在将对象文字指定给扩展对象文字的泛型类型时会抱怨?,typescript,typescript-typings,Typescript,Typescript Typings,我不明白为什么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 paramete

我不明白为什么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;
}
关于代码的一些注释:

  • 我希望
    T
    始终是一个对象
    {}
    ,因此任何人都不能执行
    useComplexState
  • 理想情况下,
    T
    不应该是必需的,所以如果我使用
    useComplexState()
    我应该在里面有一个通用对象
    记录
  • 我希望
    状态的默认值为
    {}


useState({}ast)中添加
ast
修复了每个问题。

useState({}ast)中添加
ast
修复了每个问题。

因为您可以调用
useSomething()
但是
{code}
不能分配给
{foo:number}
。(请注意,
T扩展X
并不意味着所有的
X
都可以分配给
T
。相反)为什么
useSomething()
是通用的?你打算怎么称呼它呢?谢谢你的评论,但是我还是不完全理解这个问题。我知道
{foo:number}
不是
{}
,但我假设如果我用一组严格的属性定义一个接口或一个类型,情况就是这样,但在这里,我理解的是:“只要它是一个具有字符串键的对象,并且任何东西都是值,就可以了”然而,为什么这行可以工作<代码>常量t:{[key:string]:any}={}
@jcalz用一个更好的用法示例更新了这个问题。说
T
is
{[k:string]:any}
T扩展{[k:string]:any}
之间有区别。后者意味着
T
可能是一种限制性更强的类型。因此,如果我调用
useSomething()
我是说
T
肯定有一个
foo
类型的属性
number
。行
const t:{[k:string]:any}={}
工作,因为
t
的类型是
{[k:string]:any}
。我不知道如何更清楚地解释这一点:
A扩展了B
并不意味着可以将
B
类型的值分配给
A
类型的变量。相反,因为您可以调用
useSomething()
,但是
{}
不能分配给
{foo:number}
。(请注意,
T扩展X
并不意味着所有的
X
都可以分配给
T
。相反)为什么
useSomething()
是通用的?你打算怎么称呼它呢?谢谢你的评论,但是我还是不完全理解这个问题。我知道
{foo:number}
不是
{}
,但我假设如果我用一组严格的属性定义一个接口或一个类型,情况就是这样,但在这里,我理解的是:“只要它是一个具有字符串键的对象,并且任何东西都是值,就可以了”然而,为什么这行可以工作<代码>常量t:{[key:string]:any}={}
@jcalz用一个更好的用法示例更新了这个问题。说
T
is
{[k:string]:any}
T扩展{[k:string]:any}
之间有区别。后者意味着
T
可能是一种限制性更强的类型。因此,如果我调用
useSomething()
我是说
T
肯定有一个
foo
类型的属性
number
。行
const t:{[k:string]:any}={}
工作,因为
t
的类型是
{[k:string]:any}
。我不知道如何更清楚地解释这一点:
A扩展了B
并不意味着可以将
B
类型的值分配给
A
类型的变量。反过来说如果你没事的话可能会对编译器撒谎
{}
几乎肯定不是
T
类型的值。考虑到您的实现,我最多只能说您应该使用
Partial
而不是
T
。从技术上讲,您是对的,它应该是
useState({})
,因此,如果我将代码链接为单独的答案并附上解释,您会接受吗?或者你想自己修改这个答案?继续吧,我会接受的。这是我能为你做的最起码的事。是的,如果你没事的话,可能会对编译器撒谎
{}
几乎肯定不是
T
类型的值。考虑到您的实现,我最多只能说您应该使用
Partial
而不是
T
。从技术上讲,您是对的,它应该是
useState({})
,因此,如果我将代码链接为单独的答案并附上解释,您会接受吗?或者你想自己修改这个答案?继续吧,我会接受的。这是我能为你做的最起码的事。
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');