扩展静态类型的泛型不是智能感知

时间:2019-10-16 09:12:15

标签: reactjs typescript generics

handleOnChange函数不会自动完成key参数,并且不能为value参数提供正确的类型。

这不符合预期。

export function MyComponent<T extends MyItem>(props: PropsWithChildren<MyComponentProps>) {
  const { item, setItem } = props

  function handleOnChange<K extends keyof T>(key: K) {
    return function(value: T[K]) {
      setItem({ ...item, [key]: value })
    }
  }

  return <input value={item.name} onChange={e => handleOnChange('name')(e.target.value)} />
}

这确实按预期工作。

export function MyComponent<T extends MyItem>(props: PropsWithChildren<MyComponentProps>) {
  const { item, setItem } = props

  function handleOnChange<K extends keyof MyItem>(key: K) {
    return function(value: MyItem[K]) {
      setItem({ ...item, [key]: value })
    }
  }

  return <input value={item.name} onChange={e => handleOnChange('name')(e.target.value)} />
}

您知道为什么在这种情况下泛型会导致问题吗?

2 个答案:

答案 0 :(得分:0)

您是否尝试过使用K keyof T而不是K extends keyof T

答案 1 :(得分:0)

对于问题的IntelliSense部分,看起来当keyof A时检查keyof B时,完成列表只是不包括B extends A。如果可以,那会很好,但事实并非如此。 suggested是这样的更改。如果您希望看到这种情况发生,那么您可能想看一下Github上的问题,或者如果您认为它既引人注目又与现有内容不同,请描述您的用例。

目前,解决方法可能是将约束更改为K extends keyof T | keyof MyItem。由于keyof T已经必须包含keyof MyItem,因此,keyof MyItem的附加并集不会改变什么类型的K有效规范。但这确实会带回您期望的IntelliSense自动完成列表。


对于问题的“ value参数的正确类型”部分,我想我需要详细说明您的意思。但请注意:如果T extends MyItem,则T["name"]的类型可能比MyItem["name"]更窄。想象一下:

interface MyItem {
    name: string;
}

export function MyComponent<T extends MyItem>() {

    function handleOnChange<K extends keyof T | keyof MyItem>(key: K) {
        return function (value: T[K]) { }
    }

    handleOnChange("name")("Fred"); // wait a minute

}

TypeScript允许您使用任何handleOnChange("name")(xxx)作为string来调用xxx,即使这样做并不安全。观察:

interface MyItemNamedBob extends MyItem {
    name: "Bob";
}

MyComponent<MyItemNamedBob>(); // ?

类型为MyItemNamedBob的值在类型级别具有文字字符串"Bob"作为name。无法将其更改为"Fred"。对于扩展了handleOnChange("name")的通用T来说,MyItem根本无法安全地接受任何内容。在实践中,这可能并不重要,但是您应该小心,因为编译器并不总是如此。


好的,希望能有所帮助;祝你好运!

Link to code