Strict mode is actually a combination of six other flags (as of TypeScript 3.8):
严格模式实际上是六个其他标志的组合(自TypeScript 3.8起):
noImplicitAny
noImplicitAny
strictNullChecks
(since 2.0)strictNullChecks
(自2.0起)noImplicitThis
(since 2.0)noImplicitThis
(自2.0开始)strictFunctionTypes
(since 2.6)strictFunctionTypes
(自2.6起)strictPropertyInitialization
(since 2.7)strictPropertyInitialization
(自2.7版开始)strictBindCallApply
(since 3.2)strictBindCallApply
(从3.2开始)
Each option can be enabled or disabled separately. Some of them make TypeScript’s type checking better and some help make your code more readable and less error-prone.
可以分别启用或禁用每个选项。 它们中的一些使TypeScript的类型检查更好,而一些帮助使您的代码更具可读性,并且不易出错。
You can enable strict mode in your tsconfig.json
:
您可以在tsconfig.json
启用严格模式:
You can disable any option that you don’t like from the strict family in the compileOptions
as well (e.g. "noImplicitAny": false
).
您也可以在compileOptions
禁用严格系列中您不喜欢的任何选项(例如"noImplicitAny": false
)。
I think the most important flags are noImplicitAny
and strictNullChecks
. These two will really improve the type checking and readability of your code.
我认为最重要的标志是noImplicitAny
和strictNullChecks
。 这两个将真正提高代码的类型检查和可读性。
Let’s take a look at each flag.
让我们看一下每个标志。
无隐含 (noImplicitAny)
Without turning noImplicitAny
on, you are only mostly using TypeScript because now you have parts of your code that are of type any
without you even noticing.
头也不回noImplicitAny
上,你只大多采用打字稿,因为现在你有一个是类型的代码的部分any
没有你甚至没有注意到。
Because the type any
basically disables type check, you really shouldn’t have it unless you don’t have a choice. The problem is that it’s really easy to have any
in your code by mistake, so by using noImplicitAny
, you will now only have any
where you explicitly use it.
因为any
类型any
基本上禁用了类型检查,所以除非没有选择,否则您实际上不应该使用它。 问题在于,错误地在代码中包含any
非常容易,因此,通过使用noImplicitAny
,您现在将仅拥有any
在显式使用它的位置。
Let’s see how easy it is to disable type checking by mistake:
让我们看看错误地禁用类型检查有多么容易:
In this example, we define a function (fn
) that just returns what it gets. What we can easily miss is that the return type of this function is actually any
.You can see that copyOfNum
, which is just the same number as num
, is now of type any
and we can do all sorts of wrong things with it.
在此示例中,我们定义了一个函数( fn
),该函数仅返回其获取的内容。 我们很容易错过的是,该函数的返回类型实际上是any
。您可以看到与num
相同的数字copyOfNum
现在是any
类型,我们可以用它做各种错误的事情。
If we turn noImplicitAny
on, we won’t be able to define a function like fn
that returns any
implicitly and would have to define it correctly instead, resulting in copyOfNum
not losing its type:
如果打开noImplicitAny
,我们将无法定义像fn
这样的函数,该函数隐式返回any
,而必须正确定义它,从而导致copyOfNum
不会丢失其类型:
Another common way we put any
in our code without noticing is when importing an external module:
我们不加注意地将any
代码放入代码中的另一种常见方式是在导入外部模块时:
Here, we just wanted to add 1 + 1 using lodash but got a result of type any
because we didn’t have the types of lodash installed. This can be solved by installing @types/lodash
. In case we are using an external module with no available types, we can write them ourselves using declare module ‘lodash’ { /* types goes here */}
.
在这里,我们只想使用lodash加1 + 1,但是得到了any
类型的结果,因为我们没有安装lodash类型。 这可以通过安装@types/lodash
来解决。 如果我们使用的外部模块没有可用的类型,则可以使用declare module 'lodash' { /* types goes here */}
自己编写。
If we are OK with this module being of type any
or do not have the capacity right now to fix it, we can signal it to the compiler by declaring the module with no types:
如果我们对这个模块是any
类型的模块没问题,或者现在没有修复它的能力,我们可以通过声明没有类型的模块来向编译器发出信号:
declare module ‘lodash’;
strictNullChecks (strictNullChecks)
The most common JS runtime error is probably Uncaught TypeError: Cannot read property ‘foo’ of undefined.
This is caused when trying to access a property or call a method on an undefined
object.
JS最常见的运行时错误可能是Uncaught TypeError: Cannot read property 'foo' of undefined.
这是在尝试访问undefined
对象的属性或调用方法时引起的。
Luckily, TypeScript can help.
幸运的是,TypeScript可以提供帮助。
By default, null
and undefined
are assignable to all other types, while your code actually has many types that can never be null
or undefined
, making their types wrong by default.
默认情况下, null
和undefined
可以分配给所有其他类型,而您的代码实际上具有许多永远不能为null
或undefined
的类型,默认情况下会使它们的类型错误。
After enabling strictNullChecks
, the only types that can be null
or undefined
are the ones explicitly marked as so. This means you wouldn’t be able to initialize a variable without a value. For example:
启用strictNullChecks
,唯一可以为null
或undefined
类型是明确标记为null
类型。 这意味着您将无法在没有值的情况下初始化变量。 例如:
What do you get from all this extra work? Now that your types are more specific, TypeScript will be able to analyze your code flow and find potential bugs:
您从所有这些额外工作中得到什么? 现在您的类型更加具体,TypeScript将能够分析您的代码流并查找潜在的错误:
Sometimes you have to opt out of this feature. Maybe some external types are wrong, but you have no way to fix them or you want to gradually introduce this flag. You can always disable strictNullChecks
by using the !
symbol.Any property access that has the !
symbol will be ignored in this check:
有时您必须选择退出此功能。 也许某些外部类型是错误的,但是您无法修复它们,或者您希望逐步引入此标志。 您始终可以使用!
禁用strictNullChecks
!
symbol。任何具有!
属性访问 此检查将忽略符号:
Only if you enable strictNullChecks
will you be able to enable the next flag.
只有启用strictNullChecks
您才能启用下一个标志。
strictPropertyInitialization (strictPropertyInitialization)
With strictPropertyInitialization
, TypeScript will throw an error unless all class properties are initialized in the constructor or by a property initializer. This is used to help prevent unintentional access to undefined
props in your code:
使用strictPropertyInitialization
,除非所有类属性都在构造函数中或由属性初始化程序初始化,否则TypeScript将引发错误。 这用于帮助防止意外访问代码中undefined
道具:
Sometimes you can’t initialize in the class creation. For example, maybe you use an external service to fetch data. In these cases, you can ignore strictPropertyInitialization
by using the !
symbol just like you used it for strictNullChecks
:
有时您无法在类创建中进行初始化。 例如,也许您使用外部服务来获取数据。 在这些情况下,您可以使用!
来忽略strictPropertyInitialization
!
符号,就像您将其用于strictNullChecks
:
strictFunctionTypes (strictFunctionTypes)
In TypeScript, argument types are bivariant (both covariant and contravariant), which is unsound (although you can now fix this in TypeScript 2.6 with --strictFunctionTypes
or --strict
).
在TypeScript中, 参数类型是双变量的 (协变和反变量),是不可靠的 (尽管您现在可以在TypeScript 2.6中使用--strictFunctionTypes
或--strict
来解决此问题)。
Let’s look at a simple example to understand what it means:
让我们看一个简单的示例,以了解其含义:
In this example, we call forEach
with another function that gets an HTMLElement
even though querySelectorAll
returns a list of Element
(which HTMLElement
extends). You can see that we access the element’s offsetHeight
, a property that doesn’t exist on Element
but only on HTMLElement
.
在此示例中,即使querySelectorAll
返回一个Element
列表( HTMLElement
扩展),我们也会使用另一个获取HTMLElement
函数调用forEach
。 您可以看到我们访问了元素的offsetHeight
,该属性在Element
上不存在,而仅在HTMLElement
。
Without strictFunctionTypes
, this code will compile, but its types are wrong and can lead to potential bugs. If we turnstrictFunctionTypes
on, we will get an error:
如果没有strictFunctionTypes
,则此代码将编译,但是其类型是错误的,并且可能导致潜在的错误。 如果打开strictFunctionTypes
,将收到错误消息:
Type ‘Element’ is missing the following properties from type ‘HTMLElement’ …
This basically means that we can’t decide that element
is HTMLElement
instead of Element
because then we can use properties that only HTMLElement
has inside our function, which is wrong.
这基本上意味着我们不能确定element
是HTMLElement
而不是Element
因为那样我们就可以使用函数内部只有HTMLElement
具有的属性,这是错误的。
strictBindCallApply (strictBindCallApply)
With the strictBindCallApply
flag, the bind
, call
, and apply
methods are strongly typed. Without this flag, you can mistakenly use these three functions with arguments that don’t match the function they are used on:
使用strictBindCallApply
标志,将对bind
, call
和apply
方法进行强类型化。 如果没有此标志,则可能错误地将这三个函数的参数与使用它们的函数不匹配:
noImplicititThis (noImplicitThis)
This acts the same way as noImplicitAny
, but for the this
type. If you have a function that uses this
, but the compiler can’t infer what the type of this
is from the code, you will have an error. This can help catch bugs in compile time instead of in runtime. For example:
此行为与noImplicitAny
相同,但对于this
类型。 如果你有一个使用功能this
,但是编译器不能推断出的类型this
是从代码,你将有一个错误。 这可以帮助在编译时而不是在运行时捕获错误。 例如:
In this example, we get a runtime error:
在此示例中,我们得到一个运行时错误:
Uncaught TypeError: Cannot read property ‘x’ of undefined.
We get an error because we called isEqual
with the wrong this
parameter (we lost the this
binding when we assigned the function to another variable in line 11).
由于使用错误的this
参数调用isEqual
,我们得到了一个错误(将函数分配给第11行的另一个变量时,我们失去了this
绑定)。
If we had the noImplicitThis
flag on, instead of a runtime error, we would get a compile-time error on line 6: ‘this’ implicitly has type ‘any’
. We can fix this error by writing the this
type explicitly, which will force us to fix the bug:
如果打开noImplicitThis
标志,而不是运行时错误,我们将在第6行得到编译时错误: 'this' implicitly has type 'any'
。 我们可以通过显式编写this
类型来修复此错误,这将迫使我们修复该错误:
结论 (Conclusion)
I hope you now understand strict mode and that you should use it in your code because the benefits are greater than the price — at least for new projects that don’t require migration.
我希望您现在了解严格的模式,并且应该在代码中使用它,因为这样做的好处大于价格,至少对于不需要迁移的新项目而言。
For existing projects, consider adding one strict flag at a time, making the migration more gradual.
对于现有项目,请考虑一次添加一个严格的标志,以使迁移更加逐步。