- Description : There is currently no Chinese translation of the latest official documents of TypeScript on the Internet, so there is such a translation plan. Because I am also a beginner in TypeScript, I cannot guarantee that the translation will be 100% accurate. If there are errors, please point out in the comment section;
- translation content : The tentative translation content is TypeScript Handbook , and other parts of the translation document will be added later;
- project address : TypeScript-Doc-Zh , if it helps you, you can click a star~
The official document address of this chapter: The Basics
Base
Welcome to the first chapter of the manual. If this is your first contact with TypeScript, you may need to read the Getting Started Guide
Each value in JavaScript will exhibit a series of behaviors as we perform different operations. This sounds very abstract. Look at the following example and consider the possible operations message
// 访问 message 的 toLowerCase 方法并调用它
message.toLowerCase()
// 调用 message 函数
message()
The first line of code accesses the message
method of toLowerCase
and calls it; the second line of code directly calls the message
function.
But let us assume that we don't know message
-this is a very common situation, and we can't exactly know the final result from the above code alone. The result of each operation depends entirely on the initial value message
message
be called?- Does it have
toLowaerCase
attributes? - If there is this attribute, can it be called?
- If
message
and its attributes are all callable, what will be returned?
When writing JavaScript code, the answers to these questions often need to be kept in our minds, and we must pray that we have handled all the details.
Suppose message
is defined like this:
const message = 'Hello World!'
You might easily guess that if we execute message.toLowerCase()
, we will get a string with the first letter lowercase.
What if the second line of code is executed? If you are familiar with JavaScript, you must have guessed it, this will throw an exception:
TypeError: message is not a function
It would be great if such mistakes can be avoided.
When we execute the code, the JavaScript runtime will calculate the type of value-what behavior and function this type has, and then decide what action to take. This is why the above code throws TypeError-it shows that the string "Hello World!"
cannot be called as a function.
For primitive types such as string
or number
, we can use the typeof
operator to calculate their type at runtime. But for types like functions, there is no corresponding runtime mechanism to calculate the type. For example, look at the following function:
function fn(x){
return x.flip()
}
It can be seen from the code that flip
, but JavaScript cannot pass this information in a way that we can check when the code is executed. For pure JavaScript to tell us what fn
will do when given specific parameters, the only way is to actually call the fn
function. This feature makes it difficult for us to make relevant predictions before the code is executed, and it also means that when we write the code, it is difficult to figure out what the code will do.
From this perspective, the so-called type actually describes what value can be safely passed to fn
, and what value will cause an error. JavaScript only provides dynamic typing-execute the code and then know what will happen.
So let's switch to a solution that uses a static type system to predict the behavior of the code before it actually executes.
Static type checking
Remember the TypeError that was thrown when we called the string as a function before? Most developers don't want to see any errors when executing code-after all these are bugs! When we write new code, we also try to avoid introducing new bugs.
If we just add a little code, save the file, rerun the code, and then see the error immediately, then we may be able to quickly locate the problem-but this is only a minority after all. We may not fully and thoroughly test, so that we did not find some potential errors! Or, if we are lucky enough to find this error, we may end up doing a large-scale refactoring and adding many different codes.
The ideal solution should be that we have a tool to find bugs before the code is executed. And this is exactly what static type checkers like TypeScript do. The static type system describes the structure and behavior of values at runtime. Static type checkers like TypeScript use the information provided by the type system and inform us when "something is wrong".
const message = 'hello!';
message()
// This expression is not callable.
// Type 'String' has no call signatures.
Still the previous code, but this time using TypeScript, it will throw an error when compiling.
Non-abnormal failure
So far, we have been talking about runtime errors-the JavaScript runtime tells us that it thinks something is abnormal. These exceptions can be thrown because the ECMAScript specification clearly specifies the behavior that should be performed in response to the exception.
For example, the specification states that trying to call something that cannot be called should throw an error. Maybe you will think this is "a matter of course", and you will think that when accessing properties that don't exist on the object, an error will also be thrown. But on the contrary, JavaScript's performance is different from what we expected. It returns undefined
.
const user = {
name: 'Daniel',
age: 26,
};
user.location; // 返回 undefined
Ultimately, we need a static type system to tell us which code is marked as bad code in this system-even if it is "valid" JavaScript code that does not cause an error immediately. In TypeScript, the following code will throw an error stating that location
not defined:
const user = {
name: 'Daniel',
age: 26,
};
user.location;
// Property 'location' does not exist on type '{ name: string; age: number; }'.
Although sometimes this means you need to weigh the content of the expression, but our purpose is to find more legal bugs in the program. And TypeScript can indeed catch many legal bugs:
For example, misspelling:
const announcement = "Hello World!";
// 你需要花多久才能注意到拼写错误?
announcement.toLocaleLowercase();
announcement.toLocalLowerCase();
// 实际上正确的拼写是这样的
announcement.toLocaleLowerCase();
Functions not called:
function flipCoin(){
// 其实应该使用 Math.random()
return Math.random < 0.5
}
// Operator '<' cannot be applied to types '() => number' and 'number'.
Or a basic logic error:
const value = Math.random() < 0.5 ? "a" : "b";
if (value !== "a") {
// ...
} else if (value === "b") {
// 永远无法到达这个分支
}
Type tools
TypeScript can catch bugs when there are errors in our code. This is great, but more importantly, it can prevent errors in our code from the very beginning.
The type checker can check whether we are accessing the correct attribute on the variable or other attribute by using the obtained information. At the same time, it can also use this information to prompt us about the attributes we might want to access.
This means TypeScript can also be used to edit code. When we type in the editor, the core type checker can provide error information and code completion. People often talk about the role of TypeScript at the tool level, and this is a typical example.
import express from "express";
const app = express();
app.get("/", function (req, res) {
// 在拼写 send 方法的时候,这里会有代码补全的提示
// res.sen...
});
app.listen(3000);
TypeScript is very powerful at the tool level, far more than just code completion and error message prompts when spelling. Editors that support TypeScript can automatically repair errors through the "quick fix" function, and refactor to produce easy-to-organize code. At the same time, it also has an effective navigation function, which allows us to jump to a place where a variable is defined, or find all references to a given variable. All these features are built on the type checker and are cross-platform, so your favorite editor is likely to also support TypeScript .
TypeScript compiler-tsc
We have been discussing type checkers, but we haven't used them so far. It's time to deal with our new friend, the TypeScript compiler tsc
First, install it via npm.
npm install -g typescript
This will install TypeScript's compiler tsc
. If you prefer to install it in the local node_modules folder, then you may need to use npx or similar tools to easily run the tsc command.
Now, let's create an empty folder and try to write our first TypeScript program hello.ts
.
// 和世界打个招呼
console.log('Hello world!');
Note that this line of code does not have any extra decorations, it looks exactly like a "hello world" program written in JavaScript. Now, let's run typescript
installation package comes with tsc
command type checking it.
tsc hello.ts
Look!
Wait, what do you "see"? We ran the tsc
command, but nothing seemed to happen! Yes, after all, there is no type error in this line of code, so of course you can't see the output of the error message in the console.
But check again-you will find that a new file has been exported. In the current directory, in addition to the hello.ts
file, there is also a hello.js
file, and the latter is a pure JavaScript file tsc
Checking hello.js
file, we can see the content produced by the .ts
// 和世界打个招呼
console.log('Hello world!');
In this example, TypeScript has almost nothing to be translated, so the code before and after translation looks exactly the same. The compiler always tries to produce clear and readable code that looks like a normal developer wrote. Although this is not an easy task, TypeScript always maintains indentation, pays attention to cross-line code, and tries to keep comments.
What if we deliberately introduce an error that will be thrown during the type checking phase? Try to rewrite the code of hello.ts
function greet(person, date) {
console.log(`Hello ${person}, today is ${date}!`);
}
greet("Brendan");
If we execute tsc hello.ts
again, the console will throw an error!
Expected 2 arguments, but got 1.
TypeScript tells us that we passed one less parameter to the greet
function-this error is very reasonable. So far, what we have written is still standard JavaScript code, but type checking can still find problems in our code. Thanks TypeScript!
Documents are still produced when an error is reported
One thing you may not have noticed is that in the above example, our hello.js
file has been changed again. Open this file, you will find that the content is the same as the input file content. This may be a bit unexpected. It is obvious that tsc
has reported an error just now. Why can the output file be compiled? But this result is actually related to the core principle of TypeScript: most of the time, developers know the code better than TypeScript.
To reiterate, type checking the code will limit the types of programs that can be run, so the type checker will weigh in to determine which code is acceptable. Most of the time this is not a problem, but sometimes these checks can hinder us. For example, imagine that you are now migrating JavaScript code to TypeScript code, and there are many type checking errors. In the end, you have to spend time solving errors thrown by the type checker, but the problem is that the original JavaScript code itself can run! Why can't they run after converting them into TypeScript code?
So in design, TypeScript will not hinder you. Of course, over time, you may want to take more defensive measures against errors, while also letting TypeScript take more strict behavior. In this case, you can enable the noEmitOnError compilation option. Try to modify your hello.ts
file and use the parameters to run the tsc
command:
tsc --noEmitOnError hello.ts
Now you will find that hello.js
has not been changed.
Explicit type
So far, we have not told TypeScript person
and date
are. Modify the code, the statement person
is string
type, data
is Date
object. We will also call the toDateString
method date
function greet(person: string, date: Date) {
console.log(`Hello ${person}, today is ${date.toDateString()}!`);
}
What we have done is to add type annotations person
and date
, describing what type of parameters should be accepted when calling greet
You can interpret this signature as " greet
accepts string
type person
, and Date
type date
".
With type annotations, TypeScript can tell us under what circumstances greet
may be incorrect. for example:
function greet(person: string, date: Date) {
console.log(`Hello ${person}, today is ${date.toDateString()}!`);
}
greet("Maddison", Date());
// Argument of type 'string' is not assignable to parameter of type 'Date'.
TypeScript reports an error indicating that there is a problem with the second parameter. why?
Because calling the Date
method directly in JavaScript returns a string, but calling it through new can return a Date
object as expected.
Anyway, we can quickly fix this error:
function greet(person: string, date: Date) {
console.log(`Hello ${person}, today is ${date.toDateString()}!`);
}
greet("Maddison", new Date());
Remember, we don't always need to type annotations explicitly. In many cases, even if type annotations are omitted, TypeScript can infer the type for us.
let msg = 'hello there!';
//^^^
// let msg: string
Even if we did not tell TypeScript that msg
is a string
type 06198ab12d9588, it can infer it by itself. This is a feature. When the type system can correctly infer the type, it is best not to add type annotations manually.
Note: In the editor, put the mouse on the variable, there will be a prompt about the variable type
Type of erasure
Let's take a look at what the JavaScript code produced by greet
"use strict";
function greet(person, date) {
console.log("Hello " + person + ", today is " + date.toDateString() + "!");
}
greet("Maddison", new Date());
Two changes can be noticed:
- The type annotations of the
person
anddate
- The template string becomes a string spliced
+
I'll explain the second point later, let's look at the first change first. Type annotations do not belong to the content of JavaScript or ECMAScript, so no browser or runtime can directly execute TypeScript code without processing. This is why TypeScript first needs a compiler-it needs to be compiled in order to remove or convert TypeScript's unique code, so that these codes can run on the browser. Most of the code unique to TypeScript will be erased. In this example, you can see that the type annotation code is completely erased.
Remember: type annotations will never change the behavior of the program at runtime
Downgrade
Another change is that our template string changes from:
`Hello ${person}, today is ${date.toDateString()}!`;
became:
"Hello " + person + ", today is " + date.toDateString() + "!";
Why is it like this?
Template strings are new features introduced by ECMAScript 2015 (or ECMAScript6, ES2015, ES6, etc.). TypeScript can rewrite the code of a higher version of ECMAScript into a lower version code like ECMAScript3 or ECMAScript5 (that is, ES3 or ES5). In this way, downgrading a newer or "higher" version of ECMAScript to an older or "lower" version of the code is the so-called "downgrade".
By default, TypeScript will be converted to ES3 code, which is a very old version of ECMAScript. We can use the target option to convert the code to a newer ECMAScript version. By using the --target es2015
parameter, we can get the target code of the ECMAScript2015 version, which means that these codes can be executed in an environment that supports ECMAScript2015. Therefore, tsc --target es2015 hello.ts
, we will get the following code:
function greet(person, date) {
console.log(`Hello ${person}, today is ${date.toDateString()}!`);
}
greet("Maddison", new Date());
Although the default target code uses ES3 syntax, most browsers now support ES2015. Therefore, developers can safely specify that the target code adopts ES2015 or a higher ES version, unless you need to focus on compatibility with some ancient browsers.
Strictness
Different users will choose to use TypeScript's type checker for different reasons. Some users are looking for a looser and optional development experience. They want type checking to only apply to part of the code, while still enjoying the features provided by TypeScript. This is also the development experience provided by TypeScript by default. The type is optional. The null/undefined
type will be inferred, and the value of the potential 06198ab12d98c9 type will not be checked. Just like tsc
can still produce files normally in the case of compilation errors, these default configurations will ensure that your development process will not be hindered. If you are migrating existing JavaScript code, then this configuration may be just right.
On the other hand, most users prefer TypeScript to check code quickly and as much as possible, which is why this language provides strict settings. These strict settings convert static type checking from a toggle switch mode (for your code, either check all or no check at all) to a mode close to a dial. The more you turn it, the more things TypeScript will check for you. This may require additional work, but in the long run, it is worth it, and it can lead to more thorough inspections and more sophisticated tools. If possible, new projects should always enable these strict configurations.
TypeScript has several strictness settings related to type checking. They can be turned on or off at any time. Unless otherwise specified, the examples in our document are executed with all strictness settings turned on. CLI in strict configuration item, or tsconfig.json in "strict: true"
configuration items, one-time can open all strictness. Of course, we can also turn on or turn off a setting individually. Among all these settings, noImplicitAny and strictNullChecks particularly important.
noImplicitAny
Recall that in some of the previous examples, TypeScript did not perform type inference for us. At this time, the variable will adopt the broadest type: any
. This is not the worst thing-after all, using the any
type is basically the same as pure JavaScript.
However, using any
usually runs counter to the purpose of using TypeScript. The more types you use in your program, the more you gain in verification and tools, which means you will encounter fewer bugs when coding. Enabling the noImplicitAny configuration item will throw an error when encountering a variable that is implicitly inferred to any
strictNullChecks
By default, null
and undefined
can be assigned to any other type. This will make your coding easier, but countless bugs in the world are caused by forgetting to deal with null
and undefined
-sometimes it even brings billions of dollars in losses ! strictNullChecks configuration item makes the process of handling null
and undefined
more obvious, which will make us always pay attention to whether we forget to handle null
and undefined
.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。