我有以下代码片段:

// Library code

type Forced = {
    name: string;
}

const funcExample = <T extends Forced,>(
    values: T[],
    defaultValue: (name: string) => T,
): T[] => {
    // Do some operations...
    const result = values.map( (value) => {
        // Yay, I can access my .name attribute on a Generic.
        // That's what I want
        if (value.name === "wrong_condition_that_leads_to_default") {
            const res = defaultValue(value.name)
            return res;
        }
        const res2 = {...value}
        return res2;
    })

    return result;
}
// User code

type Animal = {
    name: string,
    parent: string,
}

const animals: Animal[] = [
    {name: "A", parent: "Y"},
    {name: "B", parent: "X"},
    {name: "C", parent: "Z"},
]

const myVar = funcExample(
    animals,
    (name) => {
        const t = {
            name: name,
            parent: "foo",
        };
        return t;
    }
)
// Type of myVar is...
// const myVar: {
//     name: string;
//     parent: string;
// }[]

// I want it to be...
// const myVar: Animal[]

我已经找到了一个解决方案,它包括调用我的泛型funcExample,并在回调中指定正确的类型,例如...

const myVar = funcExample(
    animals,
    (name) => {
        const t: Animal = {
            name: name,
            parent: "foo",
        };
        return t;
    }
)
// Now the type is great, automaticly.
// const myVar: Animal[]

有没有一种方法可以让它自动映射?

我今天想要解决的问题是,我想让TypeScrip使以下错误不可能发生:定义一个不尊重我在defaultValue: (name: string) => T类型签名中提供的签名的回调.

编辑:

例如,如果我没有实现键parent: string,这就不会抛出错误.但我希望它抛出一个错误.

const myVar = funcExample(
    animals,
    (name) => {
        const t = {
            name: name,
        };
        return t;
    }
)

推荐答案

问题是,T当前是从defaultValue参数推断出来的,这意味着如果您返回Forced,那么TS将推断TForced,而不是更窄的值,比如Animal,不管作为values参数传入的是什么.

您希望仅从values参数推断T,从defaulValue参数推断not. 换句话说,在defaultValue的类型(name: string) => T中,您希望Tnon-inferential type parameter usage. 这是特征请求microsoft/TypeScript#14829的主题.

目前还没有内置的方法来做到这一点,但GitHub问题提供了各种用户实现的方法,这些方法至少适用于某些用例.一种方法是使用空对象类型{}将相关类型参数使用的优先级降低intersectingit,如上所述in this comment.这给了你

const funcExample = <T extends Forced>(
  values: T[],
  defaultValue: (name: string) => (T & {}),
): T[] => ⋯

或者,您可以引入一个新的类型参数U constrainedT,并使用U作为T的非推理版本,如前所述in this comment:

const funcExample = <T extends Forced, U extends T>(
  values: T[],
  defaultValue: (name: string) => U,
): T[] => 

还有其他的方法,但我将在这里停止;感兴趣的各方应该咨询microsoft/TypeScript#14829. 无论如何,这两个都适用于您的示例:

const myVarGood = funcExample(
  animals,
  (name) => {
    return { name: name, parent: "foo", };
  }
);
// const myVarGood: Animal[]

现在,输出被推断为Animal[].并且在所需位置也会出现错误:

const myVarBad = funcExample(
  animals,
  (name) => { // error! 
    return { name: name, };
  }
)

Playground link to code

Typescript相关问答推荐

如何修复VueJS中的val类型不能赋给函数

如何根据参数的值缩小函数内的签名范围?

如果我有对象的Typescript类型,如何使用其值的类型?

需要使用相同组件重新加载路由变更数据—React TSX

contextPaneTitleText类型的参数不能分配给remoteconfig类型的关键参数'""'''

通过按键数组拾取对象的关键点

从TypeScrip中的其他类型检索泛型类型

如何使所有可选字段在打印脚本中都是必填字段,但不能多或少?

防止重复使用 Select 器重新渲染

是否可以判断某个类型是否已合并为内部类型?

下载量怎么会超过下载量?

自定义 Select 组件的类型问题:使用带逗号的泛型类型<;T与不带逗号的<;T&>;时出错

Typescript 中相同类型的不同结果

为什么类型脚本使用接口来声明函数?他的目的是什么.

我的类型谓词函数不能像我预期的那样工作.需要帮助了解原因

Typescript不允许在数组中使用联合类型

如何实现允许扩展泛型函数参数的类型

为什么我会得到一个;并不是所有的代码路径都返回一个值0;switch 中的所有情况何时返回或抛出?(来自vsCode/TypeScript)

TS 排除带点符号的键

如何在 ngFor 循环中以Angular 正确动态渲染组件