如果您在下面的the typescript playground上尝试使用我的代码段,则会在第24行显示错误。
我意识到可以通过再次将命令强制转换为<IPlan[]>
来解决此问题,但是为什么这样做是必需的?
Property 'commands' in type 'Thing' is not assignable to the same property in base type 'AbstractThing'.
Type '{ plan: number; }[]' is not assignable to type 'IPlan[]'.
Type '{ plan: number; }' is not assignable to type 'IPlan'.
Types of property 'plan' are incompatible.
Type 'number' is not assignable to type '0 | 10 | 20'.
(property) Thing.commands: { plan: number; }[]
以下是代码段:
interface IPlan {
plan: 0 | 10 | 20;
}
// fine
let obj: IPlan = {
plan: 0
};
// also fine
let commands: IPlan[] = [
{
plan: 10
}
];
// not fine
abstract class AbstractThing {
commands?: IPlan[];
}
class Thing extends AbstractThing {
// this line has the error
commands = [
{
plan: 10
}
]
}
答案 0 :(得分:2)
不确定为什么,但是使用抽象类会使它认为=iferror(query(A:B,"select B where A =TRUE"),"No results found")
的定义是plan
,而不是您列出的特定值。
这两个“变通办法”将有助于确保使用正确的类型
number
和
class Thing extends AbstractThing {
commands = [
{
plan: 10
}
] as IPlan[]
}
答案 1 :(得分:1)
正如我的评论中所述,如果变量是从方法之外定义的,TypeScript不太可能干扰类型。
要解决此问题,您应该只在构造函数内部移动声明,如您所见here:
class Thing extends AbstractThing {
constructor() {
super();
this.commands = [
{
plan: 10
}
];
}
}
但这可能只是引擎中的错误
答案 2 :(得分:1)
这是一个known issue,其中属性(和方法在different issue中提到)不受contextually typed的影响或不受基类或已实现接口的类似属性的影响。当然,有一个 check 属性可以匹配,但这是在 正常类型推断发生之后发生的。因此,在下面的代码:
class Foo {
x = "hello";
}
interface Interface {
x: "hello" | "goodbye";
}
class Bar implements Interface {
x = "hello"; // error!
}
class Base {
x: "hello" | "goodbye" = "hello";
}
class Baz extends Base {
x = "hello"; // error!
}
在x
,Foo
和Bar
中,属性Baz
的推论是相同的。 "hello"
最初被解释为字符串文字,但是当将其分配给x
属性时,它就是widened to string
according to the rule:
- 为具有初始化程序且没有类型注释的...非只读属性推断的类型是初始化程序的扩展文字类型。
这对Foo
很好,但是会导致Bar
和Baz
错误,因为string
与implements
或{{1}不匹配}约束。 !几乎每个人都同意,当他们看到这不应该是这种方式时。当然,已实现的接口或扩展类应影响属性推断的方式。
好吧,他们tried and failed之前曾对此multiple times进行了修复;现在它还没有发生。显然,这些更改最终导致的麻烦超过了解决的麻烦。 here是有关所涉及问题的很好的评论。
我希望有一天会有某种更新来缓解这一痛苦点。现在,您只需要使用类型注释或其他类型提示来让编译器知道该怎么做。因此,在extends
和Bar
示例中,您可以这样做:
Baz
在class Bar implements Interface {
x: Interface['x'] = "hello"; // okay, "hello" | "goodbye"
}
中,我们使用了显式类型注释使Bar
与Bar["x"]
完全匹配(这意味着以后可以将Interface["x"]
更改为{{1} }代替x
)。当您不打算专门化属性,而只是使其兼容时,这通常是合适的。并且:
"goodbye"
在"hello"
中,我们使用class Baz extends Base {
readonly x = "hello"; // okay, readonly narrows to "hello" only
}
来提示编译器将Baz
保持尽可能的窄,因此readonly
只能是x
。它比x
窄。当您将该属性视为discriminant来区分一个子类型和另一个子类型时,通常这是适当的。
希望其中一项能为您服务。
好的,希望能有所帮助;祝你好运!
答案 3 :(得分:0)
在接口定义中,将plan属性的类型约束为值0、10、20之一。但是,当您在类中分配commands属性时:
commands = [
{
plan: 10
}
]
plan
属性的类型为number
。即使将其设置为10, type 也不会受到任何限制,因此,由于它的限制不如接口中plan
的类型,编译器会拒绝它。
您可以通过两种方法解决此问题,但最简单的方法是将对象强制转换为接口:
commands = [
(<IPlan>{
plan: 10
})
]
记住类型(或多或少)使用集合逻辑。假设您是类型检查器程序员,并且看到了文字表达式
{
plan: 10,
}
有几种解释方法。这只是出于我们的目的的两个:
这里还有各种各样的其他注意事项(空吗?其他键?),但是编译器还不够聪明,无法推断出这是因为您使用了文字'10',该类型与接口兼容。另外,该值可能会在运行时等发生变化。