我有一个对象,在某些情况下,我需要向其添加一些额外的属性。
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';
}
}
似乎obj = obj as AnotherType
不起作用,但是如果我分配给另一个变量,const anotherObj = obj as AnotherType
也可以起作用。
是否可以强制转换type
,而无需引入另一个变量?
答案 0 :(得分:2)
我想您要做的是确定一些财产关系。因为获取不同的类型并强制转换为另一种类型是错误的。我认为您的Item
类型在某些情况下可以具有一些其他属性,我们应该在类型定义中进行建模!
让我们从正确键入开始:
type ItemNoFoo = { name: string };
type ItemWithFoo = ItemNoFoo & {foo: string}
type Item = ItemNoFoo | ItemWithFoo;
我创建了Item作为ItemNoFoo
和ItemWithFoo
的并集。感谢我们可以定义对象的两种不同状态。
现在,我将创建一个函数,保护函数,它将检查我们处于ItemNoFoo
还是ItemWithFoo
状态。
const hasFoo = (item: Item): item is ItemWithFoo => item.name === 'test';
好的,所以我们现在可以询问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!
}
}
有关此方法的更多信息,您可以在这里找到-Sum types
如果碰巧我们的函数需要创建一个新的结构(例如-数据创建者或数据转换器),则应将其视为in -> out
管道。在类型中, in 是ItemNoFoo
,而 out 类型是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;
}
}
如您所见,联合类型Item
仍然很方便,因为此函数返回或保持不变的ItemNoFoo
类型实例或ItemWithFoo
。
让我们检查一下它的用途:
function f(item: ItemNoFoo) {
const finalItem = transform(item); // type of finalItem is ItemWithFoo | ItemNoFoo
}
如果进一步想确定是否要处理一个或另一个,那么可以方便使用hasFoo
保护函数(在解决方案1中定义),它将确定值是这两种类型中的哪一种。
答案 1 :(得分:1)
obj = obj as WithFoo
不起作用,因为obj
的类型为Item
,并且您要在其中存储WithFoo
类型的对象。
但是您可以使用另一个WithFoo
类型的变量并将obj
存储在其中。由于obj
是一个对象,因此两个变量都将保留对相同数据的引用:
interface Item {
name: string
}
function addProp(obj: Item) {
type WithFoo = Item & { foo?: string; }
const foo = obj as WithFoo;
if (obj.name == 'test') {
foo.foo = 'hello';
}
}
const x1: Item = { name: 'test' }
addProp(x1);
console.log(x1);
const x2: Item = { name: 'not-matching' }
addProp(x2);
console.log(x2);
Try it在线
。答案 2 :(得分:1)
1。)为什么obj = obj as WithFoo
不起作用?有什么方法可以在不引入其他变量的情况下强制转换类型?
首先,将obj
函数参数变量声明为Item
类型,因此TS不知道obj
包含类型{ foo?: string}
。您的第二个机会是编译器的分配control flow analysis,在这种情况下,由于obj
没有联合类型,因此无法将WithFoo
类型缩小为obj
。>
将类型S的值的赋值(包括声明中的初始化程序)分配给类型T的变量,将在赋值之后的代码路径中将该变量的类型更改为T并以S缩小。
以T缩小的类型T的计算如下:
如果T不是联合类型,则结果为T。
如果T是联合类型,则结果是T中每个可分配S的组成类型的联合。
这就是为什么在示例中出现错误Property 'foo' does not exist on type 'Item'
的原因。相反,following code会适当缩小并编译:
type WithFoo = { foo?: string; }
function addProp(obj: Item | WithFoo) {
obj = obj as WithFoo;
obj.foo = 'hello';
}
如果您不想进行重新分配或引入其他变量,则可以使用内联type assertion来访问foo
属性,该属性通常可在所有JavaScript expressions上使用:
(obj as WithFoo).foo = 'hello';
话虽如此,一种更安全的方法可能是假设obj
为联合类型Item | WithFoo
并使用type guards而不是强制转换(请参阅@Maciej Sikora的答案)。
2。)const anotherObj = obj as AnotherType
为什么起作用?
声明诸如const anotherObj = obj as AnotherType
之类的新变量时,编译器会自动将变量anotherObj
的类型推断为AnotherType
。 TS会进行其他检查,以确保AnotherType
与typeof obj
兼容。例如。 this无法编译:
function addProp(obj: Item) {
const anotherObj = obj as string // error (OK)
// ...
}
3。)声明变量后可以更改其类型吗?
不是,let
和const
变量cannot be redeclared(var
具有相同的类型,但在这里并不重要),这意味着声明的变量类型也不能更改。不过,可以通过控制流分析来缩小变量,请参见1。)。