Jes*_*ess 4 typescript typescript-typings
我有一个对象,在某些情况下我需要向它添加一些额外的属性。
interface Item {
name: string
}
function addProp(obj: Item) {
type WithFoo = Item & { foo?: string; }
// The following does NOT work
obj = obj as WithFoo;
if (obj.name == 'test') {
obj.foo = 'hello';
}
}
Run Code Online (Sandbox Code Playgroud)
它似乎obj = obj as AnotherType不起作用,但如果我分配给另一个变量,它就会起作用const anotherObj = obj as AnotherType。
有没有办法在不type引入另一个变量的情况下强制转换?
这里是网上游乐场
1.) 为什么不起作用obj = obj as WithFoo?有没有办法在不引入另一个变量的情况下强制转换类型?
首先obj函数参数变量是用类型声明的Item,所以TS不知道obj包含类型{ foo?: string}。您的第二次机会是编译器对分配的控制流分析,在这种情况下无法将obj类型缩小为WithFoo,因为obj没有联合类型:
将类型 S 的值赋值给类型 T 的变量(包括声明中的初始值设定项)会将该变量的类型更改为在赋值后的代码路径中由 S 缩小范围的 T。
由 S 缩小的类型 T 计算如下:
如果 T 不是联合类型,则结果为 T。
如果 T 是联合类型,则结果为 T 中可分配 S 的每个组成类型的联合。
Property 'foo' does not exist on type 'Item'这就是您在示例中收到错误的原因。相反,以下代码将正确缩小范围并编译:
type WithFoo = { foo?: string; }
function addProp(obj: Item | WithFoo) {
obj = obj as WithFoo;
obj.foo = 'hello';
}
Run Code Online (Sandbox Code Playgroud)
如果您不想重新赋值或引入另一个变量,可以使用内联类型断言foo来访问属性,这通常可用于所有JavaScript 表达式:
(obj as WithFoo).foo = 'hello';
Run Code Online (Sandbox Code Playgroud)
话虽如此,更安全的方法可能是假设obj是联合类型Item | WithFoo并使用类型防护而不是硬转换(请参阅@Maciej Sikora 的答案)。
2.) 为什么const anotherObj = obj as AnotherType有效?
当您声明一个新变量(如 )时const anotherObj = obj as AnotherType,编译器会自动将变量的类型推断anotherObj为AnotherType。TS 会进行额外的检查以确保AnotherType与typeof obj. 例如,这不会编译:
function addProp(obj: Item) {
const anotherObj = obj as string // error (OK)
// ...
}
Run Code Online (Sandbox Code Playgroud)
3.) 变量的类型在声明后可以更改吗?
不可以,let并且const变量不能重新声明(var具有相同的类型,但这里并不重要),这意味着声明的变量类型也不能更改。不过,可以通过控制流分析来缩小变量范围,请参见 1.)。
我认为你想做的是确定一些财产关系。因为仅仅获得不同的类型并将其转换为另一种类型是错误的。我认为您的Item类型在某些情况下可以具有一些附加属性,并且我们应该在类型定义中建模!
让我们从正确的打字开始:
type ItemNoFoo = { name: string };
type ItemWithFoo = ItemNoFoo & {foo: string}
type Item = ItemNoFoo | ItemWithFoo;
Run Code Online (Sandbox Code Playgroud)
ItemNoFoo我将 Item 创建为和的并集ItemWithFoo。感谢我们可以定义对象的两种不同状态。
现在我将创建一个函数,保护函数,它将检查我们是否是ItemNoFoo或ItemWithFoo状态。
const hasFoo = (item: Item): item is ItemWithFoo => item.name === 'test';
Run Code Online (Sandbox Code Playgroud)
好吧,太好了,我们现在可以询问Item属性 foo 是否在一种类型的范围内(因为我们的类型只是其他两种类型的联合)。
最终代码:
type ItemNoFoo = { name: string };
type ItemWithFoo = ItemNoFoo & {foo: string}
type Item = ItemNoFoo | ItemWithFoo;
const hasFoo = (item: Item): item is ItemWithFoo => item.name === 'test';
function addProp(obj: Item) {
if (hasFoo(obj)) {
obj.foo = 'hello'; // in if block obj is inferenced as ItemWithFoo!
}
}
Run Code Online (Sandbox Code Playgroud)
有关此方法的更多信息,您可以在这里找到 - Sum types
如果我们的函数需要创建一个新的结构,就像数据创建器或数据转换器一样,那么我们应该将其视为in -> out管道。在类型中,输入是ItemNoFoo,输出类型是ItemWithFoo| ItemWithFoo(第 1 点中的类型定义)。
function transform(obj: ItemNoFoo): Item {
if (obj.name === 'test') {
return {
...obj,
foo: 'hello'
} // here we create new value with new type
} else {
return obj;
}
}
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,联合类型仍然Item很方便,因为该函数返回或未更改的ItemNoFoo类型实例或ItemWithFoo。
让我们检查一下它的使用:
function f(item: ItemNoFoo) {
const finalItem = transform(item); // type of finalItem is ItemWithFoo | ItemNoFoo
}
Run Code Online (Sandbox Code Playgroud)
如果您想进一步确定是否要处理其中一种类型,那么可以方便地使用hasFoo保护函数(在解决方案 1 中定义),它将确定该值是这两种类型中的哪一种。
| 归档时间: |
|
| 查看次数: |
20054 次 |
| 最近记录: |