简体   繁体   English

TypeScript 不推断具体类型

[英]TypeScript not inferring specific type

In the code below, TypeScript gives an error on the map function:在下面的代码中,TypeScript 给出了map function 上的错误:

Property 'map' does not exist on type 'string |类型“字符串”上不存在属性“地图”| string[]'.细绳[]'。

But propertyValue in that context is string[] , because propertyName is 'chapters' .但是在该上下文中的propertyValuestring[] ,因为propertyName'chapters' Why doesn't TypeScript know that?为什么 TypeScript 不知道?

interface Book {
  title: string;
  chapters: string[];
}

const setBookProperty = <K extends keyof Book>(propertyName: K, propertyValue: Book[K]) => ({
  payload: { propertyName, propertyValue },
});

const reducer = (action: ReturnType<typeof setBookProperty>) => {
  switch (action.payload.propertyName) {
    case 'chapters': {
      const x = action.payload.propertyValue.map(s => s === 'Chapter 1');
    }
  }
};

ReturnType<typeof setBookProperty> is evaluated once as the type for action and the type it returns doesn't maintain any pairing between propertyName and propertyValue . ReturnType<typeof setBookProperty>作为action类型被评估一次,它返回的类型不维护propertyNamepropertyValue之间的任何配对。 Instead it is the union for all possible values of each, without any understanding that one depends on the other.相反,它是每个值的所有可能值的联合,没有任何依赖于另一个的理解。

If you inspect the type of action , you'll see that it is:如果您检查action的类型,您会看到它是:

{
    payload: {
        propertyName: "title" | "chapters";
        propertyValue: string | string[];
    };
}

Seeing that, you can understand how narrowing one won't narrow the other.看到这一点,您就会明白缩小一个不会缩小另一个。

In order to know the value of one based on the other, the type of your action needs to be a union of all valid pairings.为了知道一个基于另一个的值,您的操作类型需要是所有有效配对的联合。 This does require more code, but it will get you the result that you want.这确实需要更多代码,但它会为您提供所需的结果。

Define a generic action which gives the pairing for a specific key定义一个通用操作,为特定密钥提供配对

type ActionSpecific<K extends keyof Book> = {
    payload: {
        propertyName: K,
        propertyValue: Book[K]
    }
}

Use a mapping to get the union for all keys.使用映射获取所有键的联合。 In this case it is trivial since we only have two keys.在这种情况下,这是微不足道的,因为我们只有两个密钥。

type ActionMap = {
    [K in keyof Book]: ActionSpecific<K>
}

type ActionUnion = ActionMap[keyof Book]

ActionUnion resolves to ActionSpecific<"title"> | ActionSpecific<"chapters"> ActionUnion解析为ActionSpecific<"title"> | ActionSpecific<"chapters"> ActionSpecific<"title"> | ActionSpecific<"chapters"> so it maintains the pairing. ActionSpecific<"title"> | ActionSpecific<"chapters">所以它保持配对。 If you were to type ActionSpecific<keyof Book> instead you would get the same bad type as before where the pairing is lost.如果您改为键入ActionSpecific<keyof Book> ,您将得到与之前配对丢失相同的错误类型。

(Optional) Declare the return type of setBookProperty as ActionSpecific<K> (可选)将 setBookProperty 的返回类型声明为setBookProperty ActionSpecific<K>

const setBookProperty = <K extends keyof Book>(propertyName: K, propertyValue: Book[K]): ActionSpecific<K> => ({
    payload: { propertyName, propertyValue },
});

Use ActionUnion as the type for your reducer function's action , and the switch will be able to discriminate between members of the union.使用ActionUnion作为你的 reducer 函数的action的类型, switch将能够区分 union 的成员。

const reducer = (action: ActionUnion) => {
    switch (action.payload.propertyName) {
        case 'chapters': {
            const x = action.payload.propertyValue.map(s => s === 'Chapter 1');
        }
    }
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM