4 万字TypeScript 保姆级入门教程 (2021版)(建议收藏)
Posted 极客江南
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了4 万字TypeScript 保姆级入门教程 (2021版)(建议收藏)相关的知识,希望对你有一定的参考价值。
极客江南: 一个对开发技术特别执着的程序员,对移动开发有着独到的见解和深入的研究,有着多年的ios、android、html5开发经验,对NativeApp、HybridApp、WebApp开发有着独到的见解和深入的研究, 除此之外还精通 javascript、AngularJS、 NodeJS 、Ajax、jQuery、Cordova、React Native等多种Web前端技术及Java、php等服务端技术。
对于初学者来说,学习编程最害怕的就是,难。
那么,Typescript 是不是很难?
首先,我可以肯定地告诉你,你的焦虑是多余的。新手对学习新技术有很强的排斥心理,主要是因为基础不够扎实,然后自信心不够强。
我个人一直强调的是基础不牢,地动山摇,基础牢固,什么都是切菜。其实学习 TS 成本很低,只要你会 JS,学习 TS 就和切菜没什么区别。你要知道 TS 代码与 JS 代码有非常高的兼容性,无门槛,你把 JS 代码改为 TS 就可以运行,所以说不用担心 。
对于学习 TS 的困扰,我想这部分学生主要是因为没有接触过强类型的编程语言,导致他们认为学习TS需要定义各种类型,还有一些新概念等等,会增加学习成本。其实如果你了解过强类型语言,那么上手 TS 并不困难,即便没接触过强类型语言,学习起来其实也有一些技巧和规律可循。
TS教程目录
- 1.什么是 TypeScript (TS)?
- 2.为什么需要 TypeScript?
- 3.TypeScript特点
- 4. TypeScript 基础类型
- 4. TypeScript 数组与元祖
- 5. TypeScript 枚举类型
- 6. any-void类型
- 7. Never-object类型
- 8. 类型断言
- 9. 变量声明和解构?
- 10. 接口的基本使用
- 11.可选属性和索引签名
- 12. 函数接口和混合类型接口
- 13. 接口的继承
- 14. TypeScript 函数
- 15. 函数的声明与重载
- 16. 泛型
- 17. 泛型约束
- 18.类
- 19. 类属性修饰符
- 20.类方法修饰符
- 21. 类可选属性与参数属性
- 22. 类存取器
- 23. 抽象类
- 24. 类与接口
- 25. 类与泛型
- 26. 接口合并现象
- 27.数字枚举和字符串枚举
- 28. 枚举成员类型和联合类型
- 29.运行时和常量枚举
- 30. 类型推论
- 31. 类型兼容性
- 32. 函数兼容性
- 33. 枚举兼容性
- 34. 类兼容性
- 35. 泛型兼容性
- 36.交叉和联合类型
- 37. 类型保护
- 38. null和undefined
- 39. 类型别名
- 40. 类型别名和接口
- 41. 字面量类型
- 42.可辨识联合
- 43. 可辨识联合完整性检查
- 44. 索引类型
- 45. 映射类型
- 46. 映射类型
- 47. 分布式条件类型
- 48.infer关键字
- 49. unknown
- 50. 迭代器和生成器
- 51. 映射类型
- 52. 模块系统
- 53. 命名空间
- 54.声明合并
- 55. 装饰器
- 56. 类装饰器
- 57. defineProperty
- 58. 方法装饰器
- 59.访问器装饰器
- 60. 属性装饰器
- 61. 参数装饰器
- 62.混入
- 63. 声明
- 64. 声明文件
1.什么是 TypeScript (TS)?
-
TypeScript 简称 TS
-
TS 和 JS 之间的关系其实就是 Less/Sass 和 CSS 之间的关系
-
就像 Less/Sass 是对 CSS 进行扩展一样, TS 也是对 JS 进行扩展
-
就像 Less/Sass 最终会转换成 CSS 一样, 我们编写好的 TS 代码最终也会换成 JS
2.为什么需要 TypeScript?
-
因为 JavaScript 是弱类型, 很多错误只有在运行时才会被发现
-
而 TypeScript 是强类型, 它提供了一套静态检测机制, 可以帮助我们在编译时就发现错误
… …
3.TypeScript特点
- 支持最新的 JavaScript 新特特性
- 支持代码静态检查
- 支持诸如 C,C++,Java,Go 等后端语言中的特性
(枚举、泛型、类型转换、命名空间、声明文件、类、接口等)
4. TypeScript 基础类型
- TypeScript 支持与 JavaScript 几乎相同的数据类型,此外还提供了实用的枚举类型方便我们使用。
// 数值类型 number
let val1:number; // 定义了一个名称叫做val1的变量, 这个变量中将来只能存储数值类型的数据
val1 = 123;
// val1 = "123"; // 会报错
// 注意点: 其它的用法和JS一样
// val1 = 0x11;
// val1 = 0o11;
// val1 = 0b11;
console.log(val1);
// 布尔类型 boolean
let val2:boolean;
val2 = true;
// val2 = 1; // 会报错
console.log(val2);
// 字符串类型 string
let val3:string;
val3 = "123";
val3 = `val1=${val1}, val2==${val2}`;
console.log(val3);
4. TypeScript 数组与元祖
- 数组类型
- 元祖类型
// 数组类型
// 方式一
// 需求: 要求定义一个数组, 这个数组中将来只能存储数值类型的数据
let arr1:Array<number>; // 表示定义了一个名称叫做arr1的数组, 这个数组中将来只能够存储数值类型的数据
arr1 = [1, 3, 5];
// arr1 = ['a', 3, 5]; // 报错
console.log(arr1);
// 方式二
// 需求: 要求定义一个数组, 这个数组中将来只能存储字符串类型的数据
let arr2:string[]; // 表示定义了一个名称叫做arr2的数组, 这个数组中将来只能够存储字符串类型的数据
arr2 = ['a', 'b', 'c'];
// arr2 = [1, 'b', 'c']; // 报错
console.log(arr2);
// 联合类型
let arr3:(number | string)[];// 表示定义了一个名称叫做arr3的数组, 这个数组中将来既可以存储数值类型的数据, 也可以存储字符串类型的数据
arr3 = [1, 'b', 2, 'c'];
// arr3 = [1, 'b', 2, 'c', false]; // 报错
console.log(arr3);
// 任意类型
let arr4:any[]; // 表示定义了一个名称叫做arr4的数组, 这个数组中将来可以存储任意类型的数据
arr4 = [1, 'b', false];
console.log(arr4);
// 元祖类型
// TS中的元祖类型其实就是数组类型的扩展
// 元祖用于保存定长定数据类型的数据
let arr5:[string, number, boolean]; // 表示定义了一个名称叫做arr5的元祖, 这个元祖中将来可以存储3个元素, 第一个元素必须是字符串类型, 第二个元素必须是数字类型, 第三个元素必须是布尔类型
arr5 = ['a', 1, true];
// arr5 = ['a', 1, true, false]; // 超过指定的长度会报错
arr5 = ['a', 1, true];
console.log(arr5);
5. TypeScript 枚举类型
- 枚举类型是TS为JS扩展的一种类型, 在原生的JS中是没有枚举类型的
/*
枚举用于表示固定的几个取值
例如: 一年只有四季、人的性别只能是男或者女
*/
enum Gender{ // 定义了一个名称叫做Gender的枚举类型, 这个枚举类型的取值有两个, 分别是Male和Femal
Male,
Femal
}
let val:Gender; // 定义了一个名称叫做val的变量, 这个变量中只能保存Male或者Femal
val = Gender.Male;
val = Gender.Femal;
// val = 'nan'; // 报错
// val = false;// 报错
// 注意点: TS中的枚举底层实现的本质其实就是数值类型, 所以赋值一个数值不会报错
// val = 666; // 不会报错
// console.log(Gender.Male); // 0
// console.log(Gender.Femal);// 1
// 注意点: TS中的枚举类型的取值, 默认是从上至下从0开始递增的
// 虽然默认是从0开始递增的, 但是我们也可以手动的指定枚举的取值的值
// 注意点: 如果手动指定了前面枚举值的取值, 那么后面枚举值的取值会根据前面的值来递增
// console.log(Gender.Male); // 6
// console.log(Gender.Femal);// 7
// 注意点: 如果手动指定了后面枚举值的取值, 那么前面枚举值的取值不会受到影响
// console.log(Gender.Male); // 0
// console.log(Gender.Femal);// 6
// 注意点: 我们还可以同时修改多个枚举值的取值, 如果同时修改了多个, 那么修改的是什么最后就是什么
// console.log(Gender.Male); // 8
// console.log(Gender.Femal);// 6
// 我们可以通过枚举值拿到它对应的数字
console.log(Gender.Male); // 0
// 我们还可以通过它对应的数据拿到它的枚举值
console.log(Gender[0]); // Male
// 探究底层实现原理
/*
var Gender;
(function (Gender) {
// Gender[key] = value;
Gender[Gender["Male"] = 0] = "Male";
Gender[Gender["Femal"] = 1] = "Femal";
})(Gender || (Gender = {}));
let Gender = {};
Gender["Male"] = 0;
Gender[0] = "Male";
Gender["Femal"] = 1;
Gender[1] = "Femal";
* */
6. any-void类型
// any类型
// any表示任意类型, 当我们不清楚某个值的具体类型的时候我们就可以使用any
// 一般用于定义一些通用性比较强的变量, 或者用于保存从其它框架中获取的不确定类型的值
// 在TS中任何数据类型的值都可以负责给any类型
// let value:any; // 定义了一个可以保存任意类型数据的变量
// value = 123;
// value = "abc";
// value = true;
// value = [1, 3, 5];
// void类型
// void与any正好相反, 表示没有任何类型, 一般用于函数返回值
// 在TS中只有null和undefined可以赋值给void类型
function test():void {
console.log("hello world");
}
test();
let value:void; // 定义了一个不可以保存任意类型数据的变量, 只能保存null和undefined
// value = 123; // 报错
// value = "abc";// 报错
// value = true;// 报错
// 注意点: null和undefined是所有类型的子类型, 所以我们可以将null和undefined赋值给任意类型
// value = null; // 不会报错
value = undefined;// 不会报错
7. Never-object类型
// Never类型
// 表示的是那些永不存在的值的类型
// 一般用于抛出异常或根本不可能有返回值的函数
// function demo():never {
// throw new Error('报错了');
// }
// demo();
// function demo2():never {
// while (true){}
// }
// demo2();
// Object类型
// 表示一个对象
let obj:object; // 定义了一个只能保存对象的变量
// obj = 1;
// obj = "123";
// obj = true;
obj = {name:'lnj', age:13};
console.log(obj);
8. 类型断言
- 什么是类型断言?
- TS中的类型断言和其它编程语言的类型转换很像, 可以将一种类型强制转换成另外一种类型
- 类型断言就是告诉编译器, 你不要帮我们检查了, 相信我,我知道自己在干什么。
/*
例如: 我们拿到了一个any类型的变量, 但是我们明确的知道这个变量中保存的是字符串类型
此时我们就可以通过类型断言告诉编译器, 这个变量是一个字符串类型
此时我们就可以通过类型断言将any类型转换成string类型, 使用字符串类型中相关的方法了
* */
let str:any = 'it666';
// 方式一
// let len = (<string>str).length;
// 方式二
// 注意点: 在企业开发中推荐使用as来进行类型转换(类型断言)
// 因为第一种方式有兼容性问题, 在使用到了JSX的时候兼容性不是很好
let len = (str as string).length;
console.log(len);
9. 变量声明和解构?
-
TS中变量声明和解构和ES6一样
-
详见 ES6 相关知识
10. 接口的基本使用
-
什么是接口类型?
-
和 number,string,boolean,enum 这些数据类型一样
-
接口也是一种类型, 也是用来约束使用者的
* */
// 定义一个接口类型
interface FullName{
firstName:string
lastName:string
}
let obj = {
firstName:'Jonathan',
lastName:'Lee'
// lastName:18
};
// 需求: 要求定义一个函数输出一个人完整的姓名
// 这个人的姓必须是字符串, 这个人的名也必须是一个字符
function say({firstName, lastName}:FullName):void {
console.log(`我的姓名是:${firstName}_${lastName}`);
}
say(obj);
11.可选属性和索引签名
- 定义一个接口
interface FullName{
firstName:string
lastName:string
middleName?:string
[propName:string]:any
}
- 需求: 如果传递了 middleName 就输出完整名称, 如果没有传递middleName, 那么就输出 firstName 和 lastName
function say({firstName, lastName, middleName}:FullName):void {
// console.log(`我的姓名是:${firstName}_${lastName}`);
if(middleName){
console.log(`我的姓名是:${firstName}_${middleName}_${lastName}`);
}else{
console.log(`我的姓名是:${firstName}_${lastName}`);
}
}
// 注意点: 如果使用接口来限定了变量或者形参, 那么在给变量或者形参赋值的时候,
// 赋予的值就必须和接口限定的一模一样才可以, 多一个或者少一个都不行
// say({firstName:'Jonathan'});
// say({firstName:'Jonathan', lastName:'Lee', middleName:"666"});
// 注意点: 但是在企业开发中可以多一个也可能少一个
// 少一个或多个怎么做? 可选属性
// say({firstName:'Jonathan', lastName:'Lee', middleName:"666"});
// say({firstName:'Jonathan', lastName:'Lee'});
// 多一个或者多多个怎么做? 如果绕开TS检查
// 方式一: 使用类型断言
// say({firstName:'Jonathan', lastName:'Lee', middleName:"666", abc:'abc'} as FullName);
// 方式二: 使用变量
// let obj = {firstName:'Jonathan', lastName:'Lee', middleName:"666", abc:'abc'};
// say(obj);
// 方式三: 使用索引签名
say({firstName:'Jonathan', lastName:'Lee', middleName:"666", abc:'abc', 123:123, def:"def"});
12. 函数接口和混合类型接口
- 函数接口
- 我们除了可以通过接口来限定对象以外, 我们还可以使用接口来限定函数
/*
interface SumInterface {
(a:number, b:number):number
}
let sum:SumInterface = function (x:number, y:number):number {
return x + y;
}
let res = sum(10, 20);
console.log(res);
*/
// 混合类型接口
// 约定的内容中既有对象属性, 又有函数
// 要求定义一个函数实现变量累加
/*
let count = 0; // 会污染全局空间
function demo() {
count++;
console.log(count);
}
demo();
demo();
demo();
*/
/*
let demo = (()=>{ // 使用闭包确实可以解决污染全局空间的问题, 但是对于初学者来说不太友好
let count = 0;
return ()=>{
count++;
console.log(count);
}
})();
demo();
demo();
demo();
*/
// 在JS中函数的本质是什么? 就是一个对象
// let demo = function () {
// demo.count++;
// }
// demo.count = 0;
// demo();
// demo();
// demo();
interface CountInterface {
():void
count:number
}
let getCounter = (function ():CountInterface {
/*
CountInterface接口要求数据既要是一个没有参数没有返回值的函数
又要是一个拥有count属性的对象
fn作为函数的时候符合接口中函数接口的限定 ():void
fn作为对象的时候符合接口中对象属性的限定 count:number
* */
let fn = <CountInterface>function () {
fn.count++;
console.log(fn.count);
}
fn.count = 0;
return fn;
})();
getCounter();
getCounter();
getCounter();
13. 接口的继承
- 接口的继承
- TS中的接口和JS中的类一样是可以继承的
interface LengthInterface {
length:number
}
interface WidthInterface {
width:number
}
interface HeightInterface {
height:number
}
interface RectInterface extends LengthInterface,WidthInterface,HeightInterface {
// length:number
// width:number
// height:number
color:string
}
let rect:RectInterface = {
length:10,
width:20,
height:30,
color:'red'
}
14. TypeScript 函数
- TS中的函数
- TS中的函数大部分和JS相同
// 命名函数
function say1(name) {
console.log(name);
}
// 匿名函数
let say2 = function (name) {
console.log(name);
}
// 箭头函数
let say3 = (name) => {
console.log(name);
}
// 命名函数
function say1(name:string):void {
console.log(name);
}
// 匿名函数
let say2 = function (name:string):void {
console.log(name);
}
// 箭头函数
let say3 = (name:string):void =>{
console.log(name);
}
15. 函数的声明与重载
// TS函数完整格式
// 在TS中函数的完整格式应该是由函数的定义和实现两个部分组成的
/*
// 定义一个函数
let AddFun:(a:number, b:number)=>number;
// 根据定义实现函数
AddFun = function (x:number, y:number):number {
return x + y;
};
let res = AddFun(10, 20);
console.log(res);
*/
/*
// 一步到位写法
let AddFun:(a:number, b:number)=>number =
function (x:number, y:number):number {
return x + y;
};
let res = AddFun(20, 20);
console.log(res);
*/
/*
// 根据函数的定义自动推导对应的数据类型
let AddFun:(a:number, b:number)=>number =
function (x, y) {
return x + y;
};
let res = AddFun(20, 20);
console.log(res);
*/
// TS函数声明
/*
// 先声明一个函数
type AddFun = (a:number, b:number)=>number;
// 再根据声明去实现这个函数
// let add:AddFun = function (x:number, y:number):number {
// return x + y;
// };
let add:AddFun = function (x, y) {
return x + y;
};
let res = add(30, 20);
console.log(res);
*/
// TS函数重载
// 函数的重载就是同名的函数可以根据不同的参数实现不同的功能
/*
function getArray(x:number):number[] {
let arr = [];
for(let i = 0; i <= x; i++){
arr.push(i);
}
return arr;
}
function getArray(str:string):string[] {
return str.split('');
}
*/
// 定义函数的重载
function getArray(x:number):number[];
function getArray(str:string):string[];
// 实现函数的重载
function getArray(value:any):any[] {
if(typeof value === 'string'){
return value.split('');
}else{
let arr = [];
for(let i = 0; i <= value; i++){
arr.push(i);
}
return arr;
}
}
// let res = getArray(10);
let res = getArray('www.it666.com');
console.log(res);
16. 泛型
- 什么是泛型?
- 在编写代码的时候我们既要考虑代码的健壮性, 又要考虑代码的灵活性和可重用性
- 通过TS的静态检测能让我们编写的代码变得更加健壮, 但是在变得健壮的同时却丢失了灵活性和可重用性
所以为了解决这个问题TS推出了泛型的概念 - 通过泛型不仅可以让我们的代码变得更加健壮, 还能让我们的代码在变得健壮的同时保持灵活性和可重用性
// 需求: 定义一个创建数组的方法, 可以创建出指定长度的数组, 并且可以用任意指定的内容填充这个数组
/*
let getArray = (value:number, items:number = 5):number[]=>{
return new Array(items).fill(value);
};
// let arr = getArray(6, 3);
let arr = getArray("abc", 3); // 报错
console.log(arr);
*/
/*
let getArray = (value:any, items:number = 5):any[]=>{
return new Array(items).fill(value);
};
// let arr = getArray("abc", 3);
let arr = getArray(6, 3);
// console.log(arr);
// 当前存储的问题:
// 1.编写代码没有提示, 因为TS的静态检测不知道具体是什么类型
// 2.哪怕代码写错了也不会报错, 因为TS的静态检测不知道具体是什么类型
let res = arr.map(item=>item.length); // ['abc', 'abc', 'abc'] => [3, 3, 3]
console.log(res);
*/
// 需求:要有代码提示, 如果写错了要在编译的时候报错
let getArray = <T>(value:T, items:number = 5):T[]=>{
return new Array(items).fill(value);
};
// let arr = getArray<string>('abc');
// let arr = getArray<number>(6);
// 注意点: 泛型具体的类型可以不指定
// 如果没有指定, 那么就会根据我们传递的泛型参数自动推导出来
let arr = getArray('abc');
// let arr = getArray(6);
let res = arr.map(item=>item.length);
console.log(res);
17. 泛型约束
- 什么是泛型约束?
- 默认情况下我们可以指定泛型为任意类型
但是有些情况下我们需要指定的类型满足某些条件后才能指定
那么这个时候我们就可以使用泛型约束
// 需求: 要求指定的泛型类型必须有Length属性才可以
interface LengthInterface{
length:number
}
let getArray = <T extends LengthInterface>(value:T, items:number = 5):T[]=>{
return new Array(items).fill(value);
};
let arr = getArray<string>('abc');
// let arr = getArray<number>(6);
let res = arr.map(item=>item.length);
18.类
- TS中的类和ES6中的类’几乎’一样
class Person {
name:string; // 和ES6区别, 需要先定义实例属性, 才能够使用实例属性
age:number;
constructor(name:string, age:number){
this.name = name;
this.age = age;
}
say():void{
console.log(`我的名称叫${this.name}, 我的年龄是${以上是关于4 万字TypeScript 保姆级入门教程 (2021版)(建议收藏)的主要内容,如果未能解决你的问题,请参考以下文章
4 万字TypeScript 保姆级入门教程 (2021版)(建议收藏)
4 万字TypeScript 保姆级入门教程 (2021版)(建议收藏)
4 万字TypeScript 保姆级入门教程 (2021版)(建议收藏)