繁体   English   中英

Typescript 中更严格的类型断言

[英]Stricter type assertion in Typescript

所以让我们说我有

declare function doSomething(...args: any[]): any

interface Example {
    a: number
    b: number
}

doSomething({a: 2, b: 1, c: 10} as Example)

这不会报告错误,因为该对象扩展了Example并且 typescript 很高兴,所以我最终使用了一个丑陋的身份函数:

function cast<T>(arg: T) {
    return arg
}

doSomething(cast<Example>({a: 2, b: 1, c: 10})) // yay, error

这让我很恼火,我需要实际调用 %^#% 无操作函数只是为了进行正确的类型转换。 我最终在每个需要它的文件中声明它,只是为了给 js 编译器更好的优化机会。

是否有任何我不知道的 ts 魔法可以避免函数调用?

是的,我知道我可以这样做:

const x: Example = {a: 2, b: 1, c: 10}
doSomething(x)

和这个:

declare function doSomething(arg: Example): any

这真的不是重点。 考虑以下 lambda:

const example = (i: number, j: number, k: number) => cast<Example>({a: 1, b: 2, c: 3})

正确设置类型而不导致我需要编写的身份函数

const example: (i: number, j: number, k: number) => Example = (i, j, k) => ({a: 1, b: 2, c: 3})

这不是很干

是的,我可以写

function example (i: number, j: number, k: number): Example {
    return {a: 1, b: 2, c: 3}
}

再说一遍,不是重点

// 编辑

因为@thedude 只是在评论中用我不知道的 lambda 返回类型语法让我大吃一惊,还有一个我使用此转换的示例

declare function doSomethingWithArray(arg: Example[]): void

doSomethingWithArray(cast<(Example | boolean)[]>([
    {a: 1, b: 2},
    false,
    {a: 1, b: 2, c: 3}
]).filter(x => x) as Example[])

// 编辑 2

因为看起来我在解释我想要的东西方面很糟糕,另一个例子:这个通用函数解决了过滤器示例问题

function filterFalse<T>(x: (T | false)[]) {
    return x.filter(x => x) as Exclude<T, false>[]
}

doSomethingWithArray(filterFalse<Example>([
    {a: 1, b: 2},
    false,
    {a: 1, b: 2, c: 3} // error
]))

但需要为这个特定任务定义一个专门的函数。 我在问是否有一种通用方法可以强制对编译时文字进行严格的类型检查,而不会导致函数调用。 所以cast<T>做了什么,但在 js 输出中没有毫无意义的调用。

我不明白为什么在这些示例中的任何一个中都需要cast函数。 我想你可能想多了。


让我们从这里开始,这完全没问题。

doSomething({a: 2, b: 1, c: 10} as Example)

as关键字通常仍然是类型安全的。 它允许您将值转换为超类型。 在这种情况下, { a: number, b: number }{ a: number, b: number, c: number }的超类型。

例如:

const value = { a: 1, b: 2, c: 2 }
const exampleValue: { a: number, b: number } = value // works!

在您的第一个示例中,如果省略必需的属性,则会出现类型错误:

doSomething({b: 1, c: 10} as Example)
// Property 'a' is missing in type '{ b: number; c: number; }'
// but required in type 'Example'.(2352)

那么为什么这会提供错误呢?

cast<Example>({a: 1, b: 2, c: 3}) // error

因为当您创建一个值并将其分配给超类型时,您只会引用该值将属于该超类型。 这意味着可能存在的任何其他属性都将无法访问。 Example没有c属性。 所以打字稿认为这是一个错误,这是正确的。

但在实际代码中,如果你将一个带有 a、b 和 c 的对象传递给一个只需要 a 和 b 的函数,那么就不会发生任何不好的事情。 您已满足约束条件,因此没有问题。


在这个片段中:

doSomething(cast<Example>({a: 2, b: 1, c: 10})) // yay, error

剧组毫无意义。 如果doSomething关心它接收的类型,那么它应该在它的参数中声明。 如果它不在乎,那么根本不需要演员表。


const x: Example = {a: 2, b: 1, c: 10}
doSomething(x)

这与上面的as Example基本相同。


declare function doSomething(arg: Example): any

这是正确的,正是您应该做的。 如果函数采用特定类型,则让它自己强制执行。 如果您将它分配给any ,则不需要强制转换any ,因为无论如何该转换都会丢失。


const example = (i: number, j: number, k: number) =>
  cast<Example>({a: 1, b: 2, c: 3})

如果您希望函数返回类型,通常只需注释返回类型。 以下工作正常:

const example(i: number, j: number, k: number): Example =>
  ({ a: i, b: j, c: k }) // error, as expected

要正确设置类型而不导致身份函数,我需要编写:

const example: (i: number, j: number, k: number) => Example = (i, j, k) => ({a: 1, b: 2, c: 3})

不正确,请参见前面的示例。 一种功能,类型按预期强制执行。


最后:

function example (i: number, j: number, k: number): Example {
    return {a: 1, b: 2, c: 3} // error as expected.
}

如果你想要一个创建并返回一个Example的函数,这是完美的。 它正确地提醒您,您有一个永远不会被使用的属性,并清楚地记录了返回值。


域名注册地址:

您的任何代码片段都不应该需要cast<T>()函数。 我认为你最好的选择是消除所有出现的any ,然后让 Typescript 强制执行类型。 这就是它的用途。 编译器非常好,通常不需要那么多帮助。


过滤

在这一切中,过滤确实是一个奇怪的例子。 问题源于这样一个事实,即filter()被键入以返回与它开始时完全相同的数组类型,而不管您过滤什么。 常见的解决方案是将过滤器回调函数设为类型谓词函数

然后你可以做这样的事情:

interface Example {
    a: number
    b: number
}

declare function doSomethingWithArray(arg: Example[]): void

doSomethingWithArray([
    {a: 1, b: 2},
    false,
    {a: 1, b: 2, c: 3} // no error because of perfectly safe casting to supertype
].filter((x): x is Example => typeof x !== 'boolean'))

看游乐场

暂无
暂无

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

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