在函数中,内联 return 类型和单独类型定义中定义的 return 类型有什么区别?

In a function, what's the difference between an inlined return type and the return type defined in a separate type definition?

当我内联函数的 return 值类型时,如果我在单独的类型定义中定义它,我会遇到不一致的行为。

例如:

interface Foo {
    bar: string;
}

type Fooer = () => Foo;

const foo1: Fooer = () => { // It knows `.bar` should be a string
    return {
        bar: 123,
    };
}

// Return type set in a separate type definition
const foo2: Fooer = () => {
    return {
        foo: 123, // It doesn't complain about additional properties
        bar: "zzz",
    };
}

// Inline (repeated) return type
const foo3: Fooer = (): Foo => {
    return {
        foo: 123, // And now it does...
        bar: "zzz",
    };
}

Try it on the TypeScript Playground

我希望 foo2foo3 具有相同的行为(我个人希望两者显示相同的错误,或者至少是一致的)。

我在这里错过了什么?这两种方法有什么区别?

这里发生的事情是 TypeScript 'duck typing' 的结果。基本上,foo2 中的 return 类型有一个 bar 字段,它是一个字符串,这意味着该函数有一个签名 () => Promise<{foo: number, bar: string}>,可以分配给 foo2由于 duck 类型 Fooer 类型的变量。

但是,在 foo3 中,您可以认为类型检查直接应用于 return 类型,而不是整个函数。结果,您会看到错误 Object literal may only specify known properties, and 'foo' does not exist in type 'Foo'.,因为类型检查是在对象文字上执行的,它有一个额外的要求,即当您给它一个显式类型时,它不能指定未知属性。

这与 TypeScript 如何处理类型字面值有关,与函数是否异步无关——如果这些是常规函数,行为将是相同的。

一般来说,TypeScript 允许对象中存在多余的属性。如果一个对象具有所有必需的属性,那么其余的都无关紧要。异常几乎就是您发现的:类型文字和显式定义的 return 类型。