是否可以通过对象创建接口或窄类型?
例如,我想要类似的东西:
const obj : someType = {
a: "a",
b: "b",
c: {
c: "c"
}
};
type myType = narrowTypeOf obj
myType
的结果应为:
{
a: string,
b: string,
c: {
c: string
}
};
在type mytype = typeof obj
中构建的结果是(或者更幸运的是)会变成someType
的结果,因为我已经声明它是这种类型的。
如果有一种方法可以将对象用作类型定义的参数-也可以解决我的问题。
答案 0 :(得分:2)
除非SomeType
特别是union,其中一个成员是您想要的窄类型,否则它将无法正常工作。让我们首先看一下SomeType
是这样的联合的快乐情况,尽管显然这不是您的用例:
type SomeType = {
a: string,
b: string,
c: {
c: string
}
} | { foo: string } | { bar: string };
这里SomeType
是您想要的类型和两个不相关类型的并集。然后,当您将值分配给obj
并标注为SomeType
时,
const obj: SomeType = {
a: "a",
b: "b",
c: {
c: "c"
}
};
此时捕获的类型typeof obj
是您想要的类型:
type MyType = typeof obj;
/* type MyType = {
a: string;
b: string;
c: {
c: string;
};
} */
类型MyType
不包含{foo: string}
或{bar: string}
。这是因为编译器在分配时使用control flow based type analysis来缩小联合类型(有关实现此目标的PR注释,请参见microsoft/TypeScript#8010)。
但是在分配时没有基于控制流的 un-union 类型变量的变窄。 TS团队在某个时候似乎是considering it,并且有一个开放的建议(请参阅microsoft/TypeScript#16976)来缩小非联合类型的控制流范围,但目前它还不是该语言的一部分。 / p>
这意味着,如果您的SomeType
宽于您想要的宽度,但不具体包含该元素的并集,则表示您被卡住了。将变量注释为该宽类型后,对该变量的所有赋值都会忘记有关该类型当前值的任何更具体的信息。
此处正确的做法是 not 来注释变量的类型。您应该将变量注释为比所分配的值更广泛的类型的唯一原因是,如果您想稍后对变量的内容进行突变以使其成为该更大类型的其他居民。例如,如果将SomeType
给出为:
interface SomeType {
a: string | number;
b: unknown;
c: object;
}
这是为什么您将以下声明注释为SomeType
的唯一好的理由:
const obj: SomeType = {
a: "a",
b: "b",
c: {
c: "c"
}
};
如果您打算稍后再执行此操作:
obj.a = 123;
obj.b = () => 456;
obj.c = [789];
假设您不打算使用该更广泛的类型,则应让编译器推断 obj
的类型。如果您担心这将使您分配与SomeType
不兼容的内容,则可以使用辅助函数,其作用类似于缩小注释:
const annotateAs = <T>() => <U extends T>(u: U) => u;
该辅助功能可以像这样使用:
const asSomeType = annotateAs<SomeType>();
现在asSomeType()
是一个函数,它将不加宽地返回其输入,但是如果该输入不能分配给SomeType
,则会产生错误:
const oops = asSomeType({
a: "a",
b: "b",
c: "c" // error! string is not an object
})
现在您可以像这样编写obj
分配:
const obj = asSomeType({
a: "a",
b: "b",
c: {
c: "c"
}
});
成功了,所以obj
绝对是SomeType
,但是现在obj
的类型是:
type MyType = typeof obj;
/* type MyType = {
a: string;
b: string;
c: {
c: string;
};
} */
根据需要。
好的,希望能有所帮助;祝你好运!