【问题标题】:Why TypeScript complains when assigning an object literal to a generic type extending object literals?为什么 TypeScript 在将对象文字分配给扩展对象文字的泛型类型时会抱怨?
【发布时间】: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' }

示例游乐场:https://www.typescriptlang.org/play/?ssl=14&ssc=36&pln=14&pc=52#code/JYWwDg9gTgLgBAbzgVwM4FMDKMCGN0A0KGAogGZnoDG8AvnGVBCHAORTo42sDcAUHzLIAdjWARhxLM3QwAFsGEBzADwAVOOgAe+YQBNUiOAG0A1ugCeALjioYURUoC6NnMItxaAPgAUAShsNBD44OCoJOxM7PEJbWWwYpzgAXikE-HVfBFo-fhCpckoaH38Ur0R80IwYdPQfJDl0ABsmiBtWAHdoJr1WT1z8nLzQjhhkKElo-H5aAT5w4UiYGyQzSxs7B2UXODcPelTs-gWlgCYVk3NrW3tHHb3PFKNGlra2LqgevtoeIA

编辑:一个更好的例子,问题基本上是,是否可以在 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&lt;boolean&gt;
  • 理想情况下,T 不应该是必需的,所以如果我使用 useComplexState(),我应该只在里面有一个通用对象 Record&lt;string, any&gt;
  • 我希望state 的默认值为{}

【问题讨论】:

  • 因为您可以调用useSomething&lt;{foo: number}&gt;(),但{} 不能分配给{foo: number}。 (注意T extends X 并不意味着所有X 都可以分配给T。相反)为什么useSomething() 还是通用的?你打算怎么称呼它?
  • 感谢您的评论,但是,我仍然不完全理解这个问题。我知道{foo: number} 不是{},但我认为如果我定义一个具有一组严格属性的接口或类型,就会出现这种情况,但在这里,我的理解是:“作为只要它是一个带有字符串键和任何值的对象,都可以"
  • 那么为什么这条线会起作用呢? const t: { [key: string]: any } = {};
  • @jcalz 用更好的用法示例更新了问题。
  • T is {[k: string]: any}T extends {[k: string]: any} 是有区别的。后者意味着T 可能是更严格的类型。所以如果我打电话给useSomething&lt;{foo: number}&gt;() 我是说T 肯定有foo 类型为number 的属性。 const t: {[k: string]: any} = {} 行有效,因为 t 的类型 {[k: string]: any}。我不知道如何更清楚地解释这一点:A extends B 并不意味着您可以将B 类型的值分配给A 类型的变量。反之亦然。

标签: typescript typescript-typings


【解决方案1】:

useState&lt;T&gt;({} as T) 中添加as T 修复了所有问题。

【讨论】:

  • 是的,如果你没问题,可能会对编译器撒谎。 {} 几乎肯定不是 T 类型的值。充其量,鉴于您的实现,我会说您应该使用 Partial&lt;T&gt; 而不是 T
  • 你说得对,应该是useState&lt;Partial&lt;T&gt;&gt;({})
  • 那么,如果我将链接here 的代码作为单独的答案发布并附有解释,您会接受吗?还是您想自己修改这个答案?
  • 去吧,我会接受的。这是我能做的最起码的事。
猜你喜欢
  • 1970-01-01
  • 2018-01-05
  • 2020-11-12
  • 2019-06-08
  • 2017-09-08
  • 1970-01-01
  • 2020-06-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多