首页 > 解决方案 > TypeScript 不推断特定类型

问题描述

在下面的代码中,TypeScript 给出了map函数错误:

类型“字符串”上不存在属性“地图”| 细绳[]'。

propertyValue在那种情况下是string[],因为propertyName'chapters'。为什么 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');
    }
  }
};

标签: typescript

解决方案


ReturnType<typeof setBookProperty>作为 for 的类型被评估一次,action并且它返回的类型不维护 and 之间的任何propertyName配对propertyValue。相反,它是每个值的所有可能值的联合,没有任何理解一个依赖于另一个。

如果您检查 的类型action,您会看到它是:

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

看到这一点,您可以理解如何缩小一个不会缩小另一个。

为了知道一个基于另一个的值,您的操作类型需要是所有有效配对的联合。这确实需要更多的代码,但它会得到你想要的结果。

定义一个通用操作,为特定键提供配对

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

使用映射来获取所有键的联合。在这种情况下,这很简单,因为我们只有两个键。

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

type ActionUnion = ActionMap[keyof Book]

ActionUnion解析为,ActionSpecific<"title"> | ActionSpecific<"chapters">因此它保持配对。如果您改为键入ActionSpecific<keyof Book>,您将得到与之前丢失配对的相同错误类型。

(可选)声明返回类型setBookPropertyActionSpecific<K>

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

ActionUnion用作减速器函数的类型action,并且将switch能够区分联合成员。

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

推荐阅读