什么是TypeScript?

TypeScript是微软开发的一门编程语言,它是JavaScript的超集,即它基于JavaScript,拓展了JavaScript的语法,遵循ECMAScript规范(ES6/7/8+)。

TypeScript = Type + Script(标准JS),它可以编译成纯JavaScript,已经存在的JavaScript也可以不加改动地在TS的环境上运行。

目前, Angular 已经使用 TypeScript 重构了代码,另一大前端框架 Vue 的3.0版本也将使用 TypeScript 进行重构。在可预见的未来,TypeScript 将成为前端开发者必须掌握的开发语言之一。

为什么要使用TypeScript?

  1. 提升开发效率。
  2. 提升可维护性。
  3. 提升线上运行质量。TS有编译期的静态检查,加上IDE的智能纠错,尽可能的将BUG消灭在编译器上,线上运行时质量更稳定可控。
  4. 可读性强,适合团队协作

TypeScript开发环境

npm install -g typescript  // 安装ts编译器
tsc hello.ts // 手动编译ts文件,会生成同名js文件
tsc --init // 生成tsconfig.js文件

当然,我们可以配置webpack,开启node服务,进行热更新开发。

TypeScrip数据类型

学习数据类型前,要先明白两个概念:

强类型和弱类型

强类型指一个变量一旦声明,就确定了它的类型,此后不能改变它的类型。弱类型可以随便转换。TypeScript是强类型语言,JavaScript是弱类型语言。

静态类型和动态类型

静态类型语言:编译阶段检查所有数据的类型。动态类型语言:将检查数据类型的工作放在程序的执行阶段,也就是说,只有在程序执行时才能确定数据类型。

基本类型

在ES6的基础上,新增了void、any、never、元组、枚举、高级类型。

布尔、数字、字符串

let bool: boolean = true;
let num: number = 10;
let str: string = "abc";
let abc: number | boolean | null; // 可以为一个变量声明多种类型,除非有特殊需求,一般不建议这样做。

数组

TypeScript的数组,所有元素只能是同一种数据类型。

let arr1: number[] = [1,2,3];
let arr2: Array<number> = [4,5,6]; // 数组的第二种声明方式,与前面等价
let arr3: string[] = ["hello","array","object"];

元组

元组是特殊的数组,限制了元素的个数和类型。

let tuple: [number,string,boolean] = [10,"hello",true];
// tuple.push(5); // 元组越界,但不会报错。原则上不能对tuple push,这应该是一个缺陷
// console.log(tuple[3]); // 新增的元素无法访问。

函数

  • 函数的声明定义(三种方式)
  • 函数传参
  1. 可选参数必须放在必选参数的后面
  2. 使用ES6的默认参数,不需要声明类型
  3. 使用ES6的剩余参数,需要声明类型
// 方式一 箭头函数:声明和定义分开
let compute: (x:number, y:number) => number; // 函数声明,规定了传入参数、返回值的数据类型
compute = (a, b) => a+b; // 函数定义时,参数名称不必与声明时的相同
// 方式二:箭头函数:声明的同时定义
let add = (x:number, y:number) => { return x+y }; // 返回值的类型可省略,这利用了ts的类型推断功能
// 方式三:function关键字:声明和定义分开
function add (x: number,y: number): number;
// 方式四:function关键字:声明的同时定义
function add (x: number,y: number): number{ retrun x+y; }
//函数传参:
function add789(x: number, y?: number, z=1, ...rest: number[]) {
console.log(rest);
return y ? x+y : x
}

对象

// 正确的写法
let obj1: {x:number, y:number} = {x: 1, y: 2};
obj1.x = 3; // 不建议的写法
let obj: object = {x: 1, y: 2};
obj.x = 3; // 报错,因为定义的时候绕过了声明检查,此时不知道是什么类型。

symbol

let s1: symbol = Symbol();
let s2 = Symbol();

undefind、null

let un: undefined = undefined;
let nu: null = null;
nu = null; // 这样是允许的,需要将tsconfig中“strictNullChecks”置为false
un = undefined;

void

是一种操作符,可以让任意一个表达式返回undefined。之所以引进void,是因为undefined不是一个保留字,可以在局部作用域内将其覆盖掉。

let noReturn = () => {};

any

any 表示变量可以为任何类型,在TS中一般不用它。如果使用它,也便失去了使用TS的意义,与前面不建议为变量声明多种类型是一个道理。

let x: any;
x = 1;
x = "str";

never

表示永远不会有返回值的类型

let error = () => {
throw new Error("errors")
};
let endless = () => {
while(true)
{}
};
// 以上两个例子永远不会有返回值

枚举类型 enum

枚举主要来定义一些常量,方便记忆,减少硬编码,增加可读性。

基本使用:

// 数字枚举
enum Role {
Reporter, // 默认从0开始
Developer=5, // 也可指定某个值
Maintainer,
}
console.log(Role); // 可以看到数据结构,能够进行反向映射,即通过值来访问成员
console.log(Role.Developer); // 访问枚举成员 // 字符串枚举 不可以进行反向映射
enum message {
success = "成功了",
fail = "失败了"
}
console.log(message); // 异构枚举,将数字和字符串混用
enum Answer {
N = 0,
Y = "yes"
}

注意:不能修改枚举成员的值

枚举成员的分类:

枚举成员的分类:
(1)常量枚举成员
(2)对已有枚举成员的引用
(3)常量表达式
(4)非常量表达式。这种成员的值不会在编译阶段确定,在执行阶段才会有
例:
enum Char {
a,
b = 9,
c = message.success,
d = b,
e = 1 + 3,
f = Math.random(),
g = "123".length,
}
console.log(Char);

常量枚举和枚举类型

// 常量枚举 用const声明的枚举都是常量枚举。特性:在编译阶段被移除,编译后不会出现
// 作用:当我们不需要对象,只需要对象的值的时候
const enum Month{
Jan,
Feb,
Mar,
}
let month = [Month.Jan,Month.Feb,Month.Mar];
console.log(month); // 枚举类型 枚举可以作为一种类型
let e: Role = 2;
let e1: Role.Developer = 12; // 数字枚举类型与number类型相互兼容,因此可以复制
console.log(e,e1); // 按照Role的类型去声明新变量 let g1: message.success = "hello"; // 报错,字符串枚举类型message.success与string类型不兼容 e === e1; // 可比较
e === g1; // 不可比较,因为类型不一样

interface接口

接口可以用来约束对象、函数、类的结构和类型,是一种契约,并且声明之后不可改变。

1.定义 (interface关键字)

interface List {
id: number;
name: string;
} interface Result {
data: List[]; // 表示由List接口组成的数组
} function render(result:Result) {
result.data.forEach((value)=>{
console.log(value);
})
} let result = {
data:[
{id:1,name:"a"},
{id:2,name:"b"},
],
};
render(result);

2.内部规范了什么?

通过上述例子,看到接口规范了成员名称、成员的的类型、值的类型。

此外,还可以规范成员属性。

3.成员属性

可选属性和只读属性

interface List {
readonly id: number; // readonly表示只读属性
name: string;
age?: number; // ?表示可选属性
}

4.索引签名

当不确定接口中有多少属性的时候,可以用索引签名。

格式:[x: attrType]: valueType  分别规定了成员的类型和值的类型,即通过什么来索引和访问的值的类型。

一般通过数字和字符串来索引,也可以两者混合索引。

// 用数字索引
interface StringArray {
[index: number]: string; // 表示,用任意的数字去索引StringArray,都会得到一个string。这就相当于声明了一个字符串类型的数组
}
let chars: StringArray = ["A","B"]; // 此时,chars就是一个字符串数组,我们可以用下标去访问每个元素
console.log(chars,chars[0]); // 用字符串和数字混合索引
interface Names {
[x: string]: string; // 用任意的字符串去索引Names,得到的结果都是string。
// y: number; // 此时不能声明number类型的成员
// [y: number]: number // 报错,因为x和y的值string和number类型不兼容
[z: number]: any; // 两个签名的返回值类型之间要相互兼容。为了能保持类型的兼容性。
}
let names: Names = {"ming":"abc",1:"45"};
console.log(names[1],names["ming"]); // 通过数字索引、通过字符串索引

※ 注意值的类型要兼容

  (1)索引签名和普通成员

    如果设置了[x: string]: string,不能再设置y: number。如果设置了[x: string]: number不能再设置y: string

  (2)索引签名和索引签名

    如果多个索引签名的值不同,要注意相互兼容,比方any和string

5.函数传参时如何绕过类型检查

如果在接收的后端数据中,比约定好的接口多了一个字段,能否通过类型检查?会不会报错?

let result = {
data:[
{id:1,name:"a",sex:"man"},
{id:2,name:"b"},
],
};
render(result); // 这样是不会报错的,只要满足接口约定的必要条件即可 render({
data:[
{id:1,name:"a",sex:"man"},
{id:2,name:"b"},
],
}); // 但如果这样调用,会报错,因为无法通过sex:"man"的类型检查。这时候需要用其他方法

我们有三种方法:

  1. 通过接口定义变量,函数调用时传入变量名(只对必要的约定条件进行检查,多余的数据不做检查)
  2. 类型断言(所有约定都不做类型检查,失去了ts类型检查的意义)
  3. 索引签名

第一种方法已经在上面做了示例,我们看后面两种方法如何做:

// 类型断言
render({
data:[
{id:"b",name:3,sex:"man"},
{id:2,name:"b"},
],
}as Result); // 明确告诉编译器,数据符合Result,这样,编译器会绕过类型检查
render(<Result>{
data:[
{id:1,name:"a",sex:"man"},
{id:2,name:"b"},
],
}); // 与上等价,但在React中容易引起歧义。不建议使用 // 索引签名
interface List {
id: number;
name: string;
[x: string]: any; // 字符串索引签名。用任意字符串去索引List,可以得到任意的结果,这样List接口可以支持多个未知属性
}

在什么场景下用什么方法,需要我们熟知这三种方法的特性

6.接口和函数

接口可以用来定义函数的传参、返回值的类型

interface Add1 {
(x: number,y: number): number;
}
let add1: Add1 = (a,b) => a+b;

此外,还可以用类型别名来定义函数

type Add2 = (x: number,y: number) => number;
let add2: Add2 = (a,b) => a+b; // 声明+定义

我们再来总结一下函数的声明定义方式:

  1. 普通声明定义(function、箭头函数)
  2. 接口定义类型
  3. 类型别名

另外,接口内也可以定义函数

// 混合类型接口
interface Lib {
abc(): void;
version: string;
doSomething(): void;
} function getLib(){
let lib: Lib = {
abc: ()=>{},
version: "1.0",
doSomething: ()=>{}
};
// let lib: Lib = {} as Lib; // 定义的时候,这种方式更方便
lib.version = "1.0";
lib.doSomething = () => {};
return lib;
}
let lib1 = getLib();
console.log(lib1,lib1.version,lib1.doSomething());

class类

关于类的成员:

1.属性必须有类型注解

2.属性必须有初始值

3.属性修饰符:

  (1)公有 public

    所有成员默认都是public。可以通过各种方式访问。

  (2)私有 private

    私有成员只能在类中被访问,不能被实例和子类访问。如果给构造函数加上私有属性,表示这个类既不能被实例化也不能被继承。

  (3)受保护 protected

    受保护成员只能在类和子类中访问,不能通过它们的实例访问。如果给构造函数加上受保护属性,表示这个类不能被实例化只能被继承。也就是声明了一个基类。

  (4)静态 static

    静态成员只能通过类名和子类名访问,不能被实例访问。

  (5)只读 readonly

    只读成员不能被修改。

4.以上属性除了可以修饰成员,也可以修饰构造函数中的参数(static除外)。这样可以省去构造函数之中外的类型注解,简化代码。

class Dog{
constructor(name: string){
this.name = name; // 属性必须赋初值
}
name: string; // 必须要为属性添加类型注解。
run(){
console.log("running");
this.pri(); // 只能在类内部访问私有成员
this.pro();
}
private pri(){ // 私有成员只能被类本身调用,不能被类的实例和子类调用
console.log("pri是dog类的私有属性");
}
protected pro(){ // 受保护成员只能在类和子类中访问
console.log("pro是dog类的受保护属性");
}
readonly logs: string = "new";
static food: string = "food"; // 静态修饰后,只能通过类名调用,不能被子类和实例调用。静态成员可以被继承
}
let dog = new Dog("dog1");
dog.run();
console.log(Dog.food); // 通过类名访问静态成员

抽象类和多态

所谓抽象类(abstract),是只能被继承,不能被实例化的类。

抽象方法

在抽象类中,不必定义方法的具体实现,这就构成了抽象方法。在抽象类中使用abstratct关键字修饰后,不能定义该方法的具体实现。

抽象方法的好处是:实现多态。

interface AnimalParam{
bigClass: string,
environment: string,
[x: string]: string;
}
abstract class Animal{ // 抽象类用abstract关键字修饰
constructor(params: AnimalParam) { // 构造函数参数使用接口是为了其子类在定义的时候方便传参
this.bigClass = params.bigClass;
this.environment = params.environment;
}
bigClass: string;
environment: string;
abstract sleep(): void;
} class Dogs extends Animal{
constructor(props: AnimalParam){
super(props);
this.name = props.name;
}
name: string;
run(){
console.log("running");
}
sleep() {
console.log("dog sleep")
}
}
class Cat extends Animal{
sleep(): void {
console.log("cat sleep")
}
}
let dog1 = new Dogs({bigClass:"a",environment:"b",name:"xiaoqi"});
let cat = new Cat({bigClass:"a",environment:"b"}); let animals: Animal[] = [dog1,cat];
animals.forEach(i=>{
i.sleep();
});
this

我们可以在方法中返回this,可以进行链式调用,非常方便。

class WorkFlow{
step1(){
return this;
}
step2(){
return this;
}
}
class Myflow extends WorkFlow{
next(){
return this;
}
}
console.log(new WorkFlow().step1().step2());
console.log(new Myflow().next().step1().step2());

类和接口

类类型接口

  • 接口约束类成员有哪些属性,以及它们的类型
  • 类在实现接口约束的时候,必须实现接口中描述的所有内容。可以多,不可以少
  • 接口只能约束类的公有成员,即不可以将接口中规定的成员置为非公有的属性
  • 接口不能约束类的构造函数
interface Human {
name: string;
eat(): void;
}
class Asian implements Human{
constructor(name: string){
this.name = name;
}
name: string;
eat(){}
sleep(){}
}

接口继承接口

interface  Man extends Human{
run(): void
}
interface Child {
cry(): void
}
interface Boy extends Man,Child{} // 多继承,将多个接口合并成一个接口

接口继承类

可以理解为,将类转化成接口。接口集成类的时候,不仅抽离了公有成员,也抽离了私有成员、受保护成员。

如何理解呢?这么做的目的是限定接口的使用范围,并不会真正为这个接口添加类的私有和受保护属性,而这个限定范围就是:只能由子类来实现这个接口。

class Auto{
state = 1;
private state2 = 0;
protected state3 = 3;
}
interface AutoInterface extends Auto{} // 接口继承类 class C implements AutoInterface{ // C在实现这个接口的时候,无法实现接口中的私有成员和受保护成员,因此报错
state = 1
}
class Bus extends Auto implements AutoInterface{ // Auto的子类Bus,遵循AutoInterface接口
showMsg(){
// console.log(this.state2);
console.log(this.state3);
}
} let bus = new Bus();
bus.showMsg();
console.log(bus);

非空断言操作符

它会排除掉变量中的 null 和 undefined,只需要从变量后面添加 ! 即可。

function simpleExample(a: number | undefined) {
const b: number = a; // COMPILATION ERROR: undefined is not assignable to number.
const c: number = a!; // OK
}

非空断言操作符会经常在程序中用到,例如获取 ref,它会被规范为 HTMLElement | null,默认值为 null,在需要使用的时候经常会被编译器报错,提示你这个变量有可能是 null。往往我们比编译器更懂程序,在这个时候需要我们告诉编译器,这个地方一定有值。

// 不使用 !,显得有些啰嗦
const goToInput = () => ref.current && ref.current.scrollIntoView();
// 使用 !,代码更简洁
const goToInput = () => ref.current!.scrollIntoView();

TypeScript语法基础的更多相关文章

  1. TypeScript 之 基础类型、高级类型

    基础类型:https://m.runoob.com/manual/gitbook/TypeScript/_book/doc/handbook/Basic%20Types.html 高级类型:https ...

  2. JAVA 入门第一章(语法基础)

    本人初学java 博客分享记录一下自己的学习历程 java我的初步学习分为六章,有c和c++的基础学起来也简便了很多. 第一章 语法基础 第二章 面向对象 第三章 常用工具类 第四章 文件操纵 第五章 ...

  3. python基础入门一(语法基础)

    作为自己正式接触并应用的第一门编程语言,在Alex和武sir两位大王的要求下,开始了写博客总结的日子.学习编程语言是很有趣的一件事情,但有2点请一定要谨记:1.做人靠自己,码代码也必须靠自己.能不能成 ...

  4. PHP语法基础

    1.PHP语法基础 PHP标记符 <?php ?> 常亮与变量 $a = 10; 变量 可以在运行过程中修改 $a = 10; $a = 20; $b = 5; echo $a+$b; c ...

  5. Verilog语法基础讲解之参数化设计

    Verilog语法基础讲解之参数化设计   在Verilog语法中,可以实现参数化设计.所谓参数化设计,就是在一个功能模块中,对于一个常量,其值在不同的应用场合需要设置为不同的置,则将此值在设计时使用 ...

  6. C#语法基础和面向对象编程

    1.C#语法基础 http://www.cnblogs.com/tonney/archive/2011/03/16/1986456.html 2.C#与面向对象基础 很棒的资源,简明扼要,介绍的非常清 ...

  7. Lua脚本之语法基础快速入门

    要 1.基本数据类型 2.Lua中的常用语句结构以及函数 3.Lua中的常用语句结构介绍 4.Lua中的库函数 目录[-] 一.基本数据类型 二.Lua中的常用语句结构以及函数 1.Lua中的常用语句 ...

  8. Javascript语法基础

    Javascript语法基础   一.基本数据类型   JavaScript中支持数字.字符串和布尔值三种基本数据类型: 1.数字 数字型是JavaScript中的基本数据类型.在JavaScript ...

  9. LinQ 语法基础

    LINQ (Language-Integrated Query,语言集成查询). LINQ to Objects.LINQ to SQL.LINQ to DataSet和LINQ to XML,它们分 ...

随机推荐

  1. js实现字符串转JSON格式

    在浏览器前端实现字符串转JSON格式,有多种方法,总结如下: 方法1. js函数,eval() 语法: var obj = eval ("(" + txt + ")&qu ...

  2. css3系列之详解perspective

    perspective 简单来说,就是设置这个属性后,那么,就可以模拟出像我们人看电脑上的显示的元素一样.比如说, perspective:800px   意思就是,我在离屏幕800px 的地方观看这 ...

  3. Confluence未授权模板注入/代码执行(CVE-2019-3396)

    --- title: Confluence未授权模板注入/代码执行(CVE-2019-3396) tags: [poc,cve] num :g7y12 --- # 简介 --- Confluence是 ...

  4. FB的新专利竟要监看使用者的脸

    大家应该会很好奇Facebook又在搞什么新花招,这个专利的名称是"Techniques for emotion detection and content delivery",其 ...

  5. 戴尔PowerEdge T110 Ⅱ服务器U盘安装Windows Server 2019 DataCenter

    一. 下载准备 准备工作——下载Microsoft Windows Server 2019 官方简体中文激活版 (MSDN)原版iso镜像 准备工作——安装刻录软件UltraISO,单文件绿色版就够用 ...

  6. Docker笔记(七):常用服务安装——Nginx、MySql、Redis

    开发中经常需要安装一些常用的服务软件,如Nginx.MySql.Redis等,如果按照普通的安装方法,一般都相对比较繁琐 —— 要经过下载软件或源码包,编译安装,配置,启动等步骤,使用 Docker ...

  7. QT状态机

    首先吐槽下网上各种博主不清不楚的讲解 特别容易让新手迷惑 总体思想是这样的:首先要有一个状态机对象, 顾名思义,这玩意就是用来容纳状态的.然后调用状态机的start()函数它就会更具你的逻辑去执行相关 ...

  8. DedeCMS V5.7 SP2前台文件上传漏洞(CVE-2018-20129)

    DedeCMS V5.7 SP2前台文件上传漏洞(CVE-2018-20129) 一.漏洞描述 织梦内容管理系统(Dedecms)是一款PHP开源网站管理系统.Dedecms V5.7 SP2版本中的 ...

  9. java的八种数据类型

    1)四种整数类型(byte.short.int.long):    byte:8 位,用于表示最小数据单位,如文件中数据,-128~127    short:16 位,很少用,-32768 ~ 327 ...

  10. hadoop2.7+spark2.2+zookeeper3.4.简单安装

    1.zookeeper的安装##配置/etc/hosts192.168.88.130 lgh192.168.88.131 lgh1192.168.88.132 lgh2 ##安装java8 解压配置环 ...