首页 > 解决方案 > 基于输入参数类型的打字稿类型

问题描述

我正在尝试执行以下操作

const useTeams = (urlFriendlyNameOrId?: string) => {
    const [team, setTeam] = useState<>();
    // Inside of useState<> I want it to evaluate to <Team> if there is a provided string or <Team[]> otherwise
    // I have tried useState<typeof urlFriendlyNameOrId extends string ? Team : Team[]>() will just always give me Team[]
    // I have tried useState<typeof urlFriendlyNameOrId === 'string' ? Team : Team[]() get error about always being false
    ...
    return { team, setTeam };
}

它将根据参数的类型生成正确的类型。我什至已经使用泛型重写了函数,但我仍然无法让它工作。

function useTeams<T extends string | undefined>(urlFriendlyNameOrId?: T) {
    const [team, setTeam] = useState<>();
    // useState<T extends string ? Team : Team[]>()
    // useState<typeof T extends string ? Team : Team[]>()
}

标签: typescriptgenericstypescript-generics

解决方案


我不会直接回答这个问题,因为我认为你在这里走错了路。不可能有条件地使用钩子,这意味着如果你的钩子可以同时使用 - 数组和单个对象,那也是类型定义。

由于为了缩小类型,唯一的方法是设置类型保护,这不能用钩子来完成,所以需要在初始化之后设置类型保护。

看看下面的代码,我认为这是你真正想要做的:

// just example type as you haven't provided such
type Team = {
    name: string;
}

function useTeams(urlFriendlyNameOrId?: string) {
    const [teams, setTeams] = useState<Team | Team[]>(
urlFriendlyNameOrId ? {name: urlFriendlyNameOrId} : []);

    if (Array.isArray(teams)) {
        teams // array
    } else {
        teams // single team
    }
}

注意 hook 与 union 一起工作Team | Team[],我有条件地传递了初始值urlFriendlyNameOrId ? {name: urlFriendlyNameOrId} : [],并且我在之后的条件下进行类型缩小。


最后一点来自我,因为对于这种情况,我总是提倡减少临时多态性,我建议将类型简化为Team[]. 看看您的代码将如何变得更好:

function useTeams(urlFriendlyNameOrId?: string) {
    const [teams, setTeams] = useState<Team[]>(
urlFriendlyNameOrId ? [{name: urlFriendlyNameOrId}] : []);

    teams // it can be or empty or one element array of teams
}

看看我关于这个主题的文章 -功能灵活性被认为是有害的


推荐阅读