TypeScript语法基础

什么是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; // 可以为一个变量声明多种类型,除非有特殊需求,一般不建议这样做。

另外,ts字符串有一些特性:

1.多行字符串
·aa
bb
cc·
反引号包着,直接换行即可,编译成js后,会加上\n换行符
2.自动拆分字符串
在调用函数时,可以将模板字符串拆分,并且不需要写小括号
function test(a:string,b:string,c:number){console.log(a,b,c)}
test`string...${name}...${age}...` // 注意调用方式,没有括号,直接反引号
会将字符串拆分为第一个参数,name拆分成第二个参数,age为第三个参数

数组

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);

 

 

转载于:https://www.cnblogs.com/V587Chinese/p/11455862.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/247012.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

不用去验血,对照这张表,就能知道自己缺啥!千万收藏好!

很多时候 身体上出现问题 不用去医院就能知道自己缺什么 对照下面的清单 能让你省下大笔钱 身体健康少生病 赶紧看看吧 1.看 头 1.头发发黄、发焦&#xff0c;缺蛋白质&#xff1b; 2.头发稀疏无光&#xff0c;补蛋白质和维生素A&#xff1b; 3.防止白发&#xff0c;补叶酸、泛…

拖延症“治疗”方案

关于拖延症&#xff0c;有个有趣的TED演讲阐述了&#xff0c;拖延症发生的大脑状态 TED&#xff1a;为什么我们会拖延&#xff1f; http://mooc.guokr.com/talk/3199/?utm_mediumedm 拖延症治疗方案 拒绝磨蹭 四招治疗拖延症 Step1 拖延症从何而来 如何克服拖延症&#…

从图(Graph)到图卷积(Graph Convolution):漫谈图神经网络模型 (三)

本文属于图神经网络的系列文章&#xff0c;文章目录如下&#xff1a; 从图(Graph)到图卷积(Graph Convolution)&#xff1a;漫谈图神经网络模型 (一)从图(Graph)到图卷积(Graph Convolution)&#xff1a;漫谈图神经网络模型 (二)从图(Graph)到图卷积(Graph Convolution)&#x…

Getting started with caffe questions answers (摘选)

本文摘选了Getting started with caffe questions answers 部分内容&#xff0c;更多细节请下载pdf文件 getting-started-with-caffe-questions-answers.pdf caffe 资料可在百度云盘下载 链接: http://pan.baidu.com/s/1jIRJ6mU 提取密码&#xff1a;xehi Q: Is there a mi…

函数式web框架

函数式web框架 转载于:https://www.cnblogs.com/stormy/p/11488266.html

Photoshop 手动画金标准流程

下面给出Photoshop手动画金标准的流程&#xff0c; 1. 读取 图片 2. 找到套锁按钮 3. 利用套锁按钮手动画金标准 4. 点击套锁区域&#xff0c;右键新建图层 此时可以看到右侧出现新建的图层1 5. 在套锁区域添加颜色 此时可以直接选择软件右下角的背景和前景的颜色都选择为…

肺部胸片图像掩膜和伪彩色处理matlab

clc; clear; close all; Iimread(006.jpg);%读取图像 I double(I); figure(1),imshow(I,[]) figure(2),imshow(I,colormap) colorbar% newimg I; II imread(006_mixture.gif); III imresize(II,0.5); figure(3),imshow(III)mask im2bw(III, 0.5); figure(4),imshow(mask…

给 Flutter 界面切换来点特效

本文微信公众号「AndroidTraveler」首发。 背景 我们知道页面之间如果直接切换&#xff0c;会比较生硬&#xff0c;还会让用户觉得很突兀&#xff0c;用户体验不是很好。 因此一般情况下&#xff0c;页面之间的切换为了达到平滑过渡&#xff0c;都会添加动画。 另外&#xff0c…

在没实践机会的前提下,如何跨越级别

我在之前的面试过程中&#xff0c;一直会遇到这样的问题&#xff1a;比如我要面试架构师&#xff0c;但我当时工作时&#xff0c;只有机会实践高级开发的技能&#xff0c;架构师方面的技能&#xff0c;只能看理论&#xff0c;最多只能在自己电脑上搭建个脚手架项目。 这样就进入…

生成高斯图像

本文转自 http://www.cnblogs.com/tiandsp/archive/2012/02/26/2368533.html clear; m31; n31; imgzeros(m1,n1); imgdouble(img); pi3.1415926; sigma10; for i-(m/2):m/2for j-(n/2):n/2img(im/21,jn/21)(1/(2*pi*sigma*sigma))*exp(-(i*ij*j)/(2*sigma*sigma)); end end i…

缓冲流

BufferedOutputStream ————字节缓冲输出流继承自父类的共性方法public void close&#xff08;&#xff09;——关闭此输出流并释放与此流相关联的任何系统资源。public void flush&#xff08;&#xff09;——刷新此输出流并强制任何缓冲的输出字节被写出。public void w…

高斯模糊的算法(高斯权重)

本文转自&#xff1a;http://www.ruanyifeng.com/blog/2012/11/gaussian_blur.html 通常&#xff0c;图像处理软件会提供”模糊”&#xff08;blur&#xff09;滤镜&#xff0c;使图片产生模糊的效果。 “模糊”的算法有很多种&#xff0c;其中有一种叫做”高斯模糊“&…

序列化流与反序列化流

序列化流与反序列化流 用于从流中读取对象的 操作流 ObjectInputStream 称为 反序列化流 用于向流中写入对象的操作流 ObjectOutputStream 称为 序列化流 特点&#xff1a;用于操作对象。可以将对象写入到文件中&#xff0c;也可以从文件中读取对象。 1 对象序列化流Objec…

图像相似性搜索的原理

本文转自&#xff1a; 相似图片搜索的原理 相似图片搜索的原理&#xff08;二&#xff09; http://www.hackerfactor.com/blog/index.php?/archives/432-Looks-Like-It.html http://www.ruanyifeng.com/blog/2011/07/principle_of_similar_image_search.html http://blog.s…

关于无效份额 和 拒绝份额

找到一个份额&#xff1a; 显卡每计算出满足要求的一个结果时是一个FOUND SHARE&#xff08;找到一个份额&#xff09;&#xff1b; 无效份额&#xff1a; 每找到一个份额时内核会将计算结果发给矿池&#xff0c;有些内核会在将结果发给矿池时&#xff08;之前、之后或同时&…

灰度共生矩阵(GLCM)并计算能量、熵、惯性矩、相关性(matlab)(待总结)

关于灰度共生矩阵的介绍可参考 http://blog.csdn.net/chuminnan2010/article/details/22035751 http://blog.csdn.net/xuezhisd/article/details/8908824 http://blog.csdn.net/xuexiang0704/article/details/8713204 http://cn.mathworks.com/help/images/ref/imlincomb.h…

你多久更新一次简历,决定了你的收入多久能提升

提到更新简历&#xff0c;或许大家第一时间就想到换工作&#xff0c;因为只有在换工作时才会更新。一般程序员大概1年半到2年跳槽一次&#xff0c;也就是说最多2年会更新一次。 更新简历是在其中更新自己的技术和各种能拿得出手的亮点&#xff0c;所以从这意义上来讲…

文件重命名批量处理(Matlab)

第一种方法&#xff1a;eval 方法 该方法未对文件进行任何处理&#xff0c;直接重命名。文件不损失任何信息。 tic clear; close all; % edited by cmwang,if any problem,please reference the website %http://blog.csdn.net/jiandanjinxin/article/details/51513598%------…

React-router总结

版本 v3和v4有一些差距&#xff1a; https://blog.csdn.net/qq_35484341/article/details/80500237 以下的总结&#xff0c;都是基于V4的 官方文档&#xff1a;https://reacttraining.com/react-router/web/guides/quick-start 核心组件和用法 <BrowserRouter/> <H…

机器学习之 weka学习(一)weka介绍,安装和配置环境变量

本部分详情可查看博客http://blog.csdn.net/u011067360/article/details/20844443 数据挖掘开源软件&#xff1a;WEKA基础教程 Weka简介&#xff1a; Weka是由新西兰怀卡托大学开发的智能分析系统&#xff08;Waikato Environment for Knowledge Analysis&#xff09; 。在怀…