小知识,大挑战!本文正在参与“程序员必备小知识”创作活动
本文同时参与掘力星计划,赢取创作大礼包,挑战创作激励金
前言
我们系统程序的漏洞就叫 bug。世界上第一个 bug ,是 1946 年霍普发现了第一个电脑上的 bug,竟然是一只飞蛾“臭虫”。解决这些问题的过程叫做捉虫、调试,也就是 Debug。
开发环境准备
网上已经有很多详细的安装教程!
创建项目
初始化一个 TypeScript 项目
新建一个【first-ts-project】文件夹,进入文件夹,在终端上执行
npm init -y
就能在文件夹创建一个package.json文件
typescript 最新稳定版
npm install -- save-dev typescript
最新的typescript
npm install --save-dev typescript@next
创建tsconfig.json
方法一:在VScode编辑器上先创建一个index.ts文件,用VScode打开;点击VScode底部TypeScript的版本号,在弹出来的弹出框点击【创建tsconfg】即可。
{
"compilerOptions": {
"module": "commonjs",
"target": "es2020",
"jsx": "preserve",
"strictFunctionTypes": true,
"sourceMap": true
},
"exclude": [
"node_modules",
"**/node_modules/*"
]
}
方法二:利用命令行
node_modules/.bin/tsc --init --locale zh-CN
通过这种方法创建tsconfig.json包含所有编译器参数以及参数说明
工具
TS playground
Equal<X, Y>
预备知识
TS与JS的关系
-
TypeScript是JavaScript的超集
-
TypeScript提供所有JavaScript特性。并在其上层增加了TypeScript类型系统。
-
这个类型系统被设计为“可选的”这就意味着:所有合法的JavaScript代码都是合法的TypeScript代码。
TS的编译过程
-
‘TS的类型检查’ 与 ‘生成JS ’是两个独立的过程;
-
类型检查出错不影响生成JavaScript代码;
类型系统
结构类型系统( Structural Type System ) 通过类型的实际结构确定两个类型是否相等或兼容
TypeScript, Haskell, OCaml, Go,...
名义类型系统( Nominal Type System ) 通过类型的名称确定两个类型是否相等
C, C++, Java, C#, Rust,...
知识点:TS采用的是结构类型系统
结构类型系统
//结构类型系统:只要两个结构中有相同的函数就OK
class Foo {
public hi() {
console.log('hi');
}
}
class Bar() {
public hi() {
console.log('Hello');
}
}
//new一个Foo实例赋值给变量a
const a: Foo = new Foo();
//将a赋值给Bar实例再赋给b
const b: Bar = a;
//调用b上的hi()方法
b.hi(); //Works!
名义类型系统
#include<iostream>
//声明两个一样的类
class Foo {
public:
void hi() {
std::cout << "Hi" << std::endl;
}
};
class Bar {
public:
void hi() {
std::cout << "Hello" << std::endl;
}
};
int main() {
Foo foo;
Foo &fooRef = foo;
//Error: type 'Bar' cannot
//bind to a value of unrelated type 'Foo'
//将foo变量赋值给bar引用,在赋值过程中,编译器就会报错,因为两个类型是完全不相关的。名字不同就不行
Bar &bar = foo;
return 0;
}
类型注解
对比:不同语言的类型注解语法
C++放在前面
int printf(const char*,...);
Objc放在前面,加括号
- (id)initWithInt:(int)value;
Julia放在后面,加双冒号
commission(sale::Int,rate::Float64)::Float64
TS的类型注解放在后面,加单冒号
function log(message: string): void
类型与集合的关系
- 空集( Empty Type )
never = ∅ = {}
- 单元素集合( Unit Type )
Null = {null}
Undefined = {undefined}
Literal Type(字面量类型,"a",1,true)
- 有限集合
Boolean = {true, false}
- 无限集合( 近似 )
String = {a','b',...,'hello',...}
Symbol = {Symbol(...),...}
BigInt = {...,-1n, 0n, 1n,...}
Number = {-Infinity,-(2^53 - 1.),... 0,...,+2^253 - 1,Infinity}和NaN
- 全集( Universal Set )
类型联合与交叉
类型别名
类比:
-
在JS中: 可以用let、const、 var声明变量或常量
-
在TS中: 可以用type为类型声明别名 都可以理解为“某个东西”的名字,可以方便记忆,更具有描述性。
// JS 声明变量或常量
let ID= 123;
cconst PI = 3.14;
var myPI = PI;
//TS 为类型声明别名
type ID = string;
type User = {
id:ID;
name:string;
}
知识点:
- 类型别名和let变量类似,也采用块级作用域。所以,同一个作用域内不能重名。
type User = {
id:ID;
name:string;
}
type User = { }
- 内层类型别名会隐藏外层同名类型
type User = {
id:ID;
name:string;
}
//块级作用域
{
type User = {
id:ID;
nname:string;
}
}
类型拓宽、收窄
类型拓宽( Type Widening )
当你把用字面量赋值给let、var变量时,TS不用字面量类型(单元素集合)作为该变量的类型。而是从字面量类型拓宽到相应的更宽泛的类型。这个过程叫做类型拓宽。
类型收窄( Type Narrowing )
在某些情况下,TS可以更加确定变量的类型,此时它会将变量类型收窄。
知识点:
-
TS试图在类型确定性与灵活性之前取得平衡
-
TS提供一系列方法来帮助收窄类型,以提高类型的确定性:
null check、as const、instanceof、 typeof、属性检查、Tagged Union、用户类型守卫( User-defined Type Guard )、代码流分析 -
用let var声明变量时,TS认为变量未来会发生改变,所以将类型推断为相对宽泛的类型
-
用const声明常量时,TS知道常量是不会改变的,会将类型推断为最窄的字面量类型 左边是类型拓宽 右边是类型收窄
值空间与类型空间
-
类型空间是由TS的编译器tsc创建的,里面存在各种类型。tsc会根据代码实例化各种类型。
-
值空间是后JSEngine 创建的 里面存在各种运行时的值。
如何判断符号是在哪个空间?
-
转译后消失的符号→类型空间
-
作为类型注解、别名的符号→类型空间( type T = typeof Person;T就是在类型空间 const p: Person;T就是在类型空间)
-
类型断言后的符号→类型空间( target as/is HTMLElement;as/is后面的符号就是在类型空间的)
-
const, let, var后面的符号→值空间
-
class, enum,namespace后的符号→值空间+类型空间
另外,有一些操作符在两个空间都存在,但是含义完全不同:
typeof
在值空间,typeof返回后面表达式对应的JavaScript类型的字符串表示:
'string', 'number', 'bigint', 'boolean', 'symbol', 'undefined', 'object', 'function'
在类型空间,typeof返回标识符对应的TypeScript类型
[]( 索引访问操作符 Indexed Access Operator )
-
在值空间,val[field]或val.field返回val对应属性的值
-
在类型空间,Type[T]返回对应T的TS类型
string, number的子集, TS4.4之后增加了symbol和template string pattern
this关键字
-
在值空间,this指...比较复杂
-
在类型空间,this可以作为类方法的返回值来实现链式调用 &|运算符
-
在值空间表示 “按位与” 和 “按位或” (Bitwise AND, OR)
-
在类型空间表示类型的交叉和联合 const
-
在值空间用来声明常量
-
在类型空间与as连用,即“as const”常量断言,收窄类型 extends
-
在值空间用于定义子类( class A extends B )
-
在类型空间用来进行类型约束( T extends number )或接口继承 ( interface A extends B ) in
-
在值空间用于for循环( for (key in object) {...} )和判断属性是否存在( 'name' in person )
-
在类型空间用于映射类型的key的声明( Mapped Type )
结语
如以上有错误的地方,请在评论区中指出,谢谢!
小可爱们看完点个赞再走一走~~