1、ts的安装
npm install -g typescript
安装结束后查看版本是否安装成功
tsc -V
2、编写ts代码
新建文件helloworld.ts
function greeter (person) {
return 'Hello, ' + person
}
let user = 'Yee'
console.log(greeter(user))
3、需将ts代码转换为js的代码引入到html页面中
在vscode集成终端中、或者cmd中
tsc helloworld.ts//输入此代码完成ts文件转换为js文件
4、vscode自动编译ts代码转为js代码
//1、使用命令生成生成配置文件tsconfig.json
tsc --init
//2、修改tsconfig.json配置
"outDir": "./js",
"strict": false,
//3、启动监视任务:
vscode上面菜单栏中找到终端,选择运行任务,选择监视tsconfig.json
终端 -> 运行任务 -> 监视tsconfig.json
或者
//命令行开始监视模式
tsc xxx.ts -w
5、ts的基础类型
//1、布尔类型--->boolean
// 基本语法
// let 变量名 : 数据类型 = 值
let flag :boolean = true
//2、数字类型--->number
let a1:number = 10
//3、字符串类型--->string
let str:string="窗前明月光"
//4、undefined和null类型
let und: undefined = undefined
let nll: null = null
//undefined和null都可以作为其他类型的子类型,把undefined和null赋值给其他类型的变量。如number类型的变量。注意:要将tsconfig.json里的strict:false,取消严格模式,否则报错。
let num2: number = undefined
//5、数组类型
//数组定义方式1:let 变量名:数据类型[] = [值1,值2]
let arr1:number[] = [10,20,30]
//数组定义方式2:泛型的写法, let 变量名 :Array<数据类型> = [值1,值2]
let arr2:Array<number>=[10,20,30]
注意:数组定义后,里面的数据的类型必须和定义数组的类型是一致的,否则会报错。如果要在number数组中加string类型,则用元祖类型
//6、元祖类型:在定义数组的时候,类型和数据的个数一开始都已经限定了。
let arr3:[string,number,boolean] = ["元祖类型",100,true]
//注意:必须按照定义的类型顺序和数量编写。
//7、枚举类型,枚举里面的每个数据值都可以叫元素,每个元素都有自己的编号,从0开始,依次递增+1
enum Color{red,green,blue}
//定义一个Color的枚举类型的变量来接受枚举的值
let color:Color = Color.red
console.log(Color.red,Color.green,Color.blue) //0 1 2
console.log(Color[0]) //red
//也可以改变编号
enum Color{red=10,green,blue} //10 11 12
enum Color{red=10,green=100,blue=1000} //10 100 1000
//8、any类型:不确定是什么类型时,可以用any。任意类型
let str:any=100 //100
str="any类型" //any类型
//数组中存储多个数据,且个数不确定时。用any。
let arr:any[]=[100,"any类型",true]
//9、void类型,在函数声明的时候,小括号的后面使用:void,代表函数没有任何的返回值。
function show():void{
console.log("void类型")
}
show()//void类型
//10、object类型
//定义一个函数,参数是object类型,返回值也是object类型
function getObj(obj:object):object{
console.log(obj)
return {
name:"卡卡西",
age:27
}
}
console.log(getObj({name:"佐助",age:"男"}))
console.log(getObj("123")) //错误,必须传声明的object类型
11、联合类型:表示取值可以为多种类型中的一种
//需求1:定义一个函数得到一个数字或字符串值
//传入number类型或者string类型,:string表示返回值是一个string类型
function getString(str:number | string) : string{
return str.toString()
}
console.log(getString(123))
//需求2:定义一个函数,得到一个数字或字符串的长度
function getString(str:number|string):number{
//如果str本身就是string类型,那么没有必要调用toString()方法
if((<string>str).length){ //类型断言,解决str可能是number类型时报错。
//如果存在,则说明str是string类型
return (<string>str).length
或者// return (str as string).length
}else{
//此时说明str是number类型
return str.toString().length
}
}
console.log(getString(123)) //3
//类型断言:告诉编译器,我知道我自己的什么类型,也知道自己在干什么
//类型断言语法1:<类型>变量名
//类型断言语法2:值 as 类型
12、类型推断:TS会在没有明确的指定类型的时候推测出一个类型
let txt=100 //number类型
txt="类型推断" //报错,不能将string类型给到number类型
console.log(txt)
let b //ang类型
b=100
b="类型推断" //any类型,不报错
6、接口
需求:创建人的对象,需要对人的属性进行一定的约束
id是number类型,必须有,只读的
name是string类型,必须有
age是number类型,必须有
sex是string类型,可以没有
//定义一个接口,该接口作为person对象的类型使用,限定或者是约束该对象中的属性数据
interface Iperson{
//readonly表示id是只读的,是number类型
readonly id: number
name: string
age: number
sex?: string //?表示此属性可有可无
}
//定义一个对象,该对象的类型就是我定义的接口Iperson
const person: Iperson = {
id:1,
name:"小甜甜",
age:18,
//sex这个属性没有也是可以的,因为接口声明加了?
//sex:"男"
}
7、函数的类型
通过接口的方式作为函数的类型来使用
//定义一个接口,用来作为某个函数的类型使用
interface ISearchFunc{
//定义一个调用签名
(source:string,subString:string):boolean
}
//定义一个函数,该类型就是上面定义的接口
const searchString: ISearchFunc = function (source:string,subString:string):boolean{
//从source里面找到subString则返回true。否则返回false
return source.search(subString) > -1
}
//调用函数
console.log(searchString("哈哈哈哈呵呵呵呵","呵呵呵呵")) //true
8、类 类型
类的类型可以通过接口来实现
//定义一个接口
interface IFly{
//盖方法没有任何的实现(方法中什么都没有)
fly()
}
//定义一个类,这个类的类型就是上面定义的接口(实际上也可以理解为IFly接口约束了当前的这个Person类)
class Person implements IFly{
//实现接口中的方法
fly(){
console.log("我会飞了")
}
}
//实例化对象
const person =new Person()
person.fly()
//定义一个接口
interface ISwim{
swim()
}
//定义一个类,这个类的类型就是IFly和ISwim(当前这个类可以实现多个接口,一个类同时也可以被多个接口进行约束)
class Person2 implements IFly,ISwim{
fly(){
console.log("我会飞了")
}
swim(){
console.log("我会游了")
}
}
//const person2= new Person2()
person2.fly()
person2.swim()
//总结:类可以通过接口的方式,来定义当前这个类的类型
//类可以实现一个接口,类也可以实现多个接口,要注意,接口中的内容都要真正的实现。
9、类
类:可以理解为模板,通过模板可以实例化对象
面向对象的编程思想
//ts中类的定义及使用
class Person{
name:string
age:number
gender:string
//定义构造函数:为了将来实例化对象的时候,可以直接对属性的值进行初始化
//给定数据类型及默认值(小天天,16,男)
constructor(name:string="小天天",age:number=16,gender:string="男”){
//更新对象中的属性数据
this.name=name
this.age=age
this.gender=gender
}
//定义实例方法
sayHi(str:string){
console.log(`我是${this.name},今年${this.age}岁了,性别${this.gender}`,str)
}
}
//ts中使用类,实例化对象,可以直接进行初始化操作
const person=new Person()
person.sayHi("你叫什么")
10、继承
继承是类与类之间的关系
class Person{
name:string
age:number
gender:string
constructor (name:string="小明",age:number=18,gender:string="男"){
this.name=name
this.age=age
this.gender=gender
}
//定义实例方法
say(str:string){
console.log(`我是${this.name},${str}`)
}
}
//定义一个类,继承Person
class Student extends Person{
constructor(name:string,age:number,gender:string){
super(name,age,gender)
}
//可以调用父类中的方法
say(){
console.log("我是学生中的say方法")
super say("哈哈")
}
}
const person = new Person("大明",19,"男")
person.say("嘎嘎")
const stu = new Student("小张",16,"女")
stu.say()
//总结:类和类之间如果要有继承关系,需要使用extends关键字
//子类中可以调用父类中的构造函数,使用的是super关键字
//子类中可以重写父类的方法
11、多态
父类型的引用指向了子类型的对象,不同类型的对象针对相同的方法,产生了不同的行为。
class Animal {
name:string
constructor(name:string){
this.name=name
}
//实例方法
run(distance:number=0){
console.log(``跑了${distance}米远)
}
}
//定义一个子类
class Dog extends Animal{
constructor(name:string){
super(name)
}
//实例方法,重写父类中的实例方法
run(distance:number=5){
console.log(``跑了${distance}米远)
}
}
//定义一个子类
class pig extends Animal{
constructor(name:string){
super(name)
}
//实例方法,重写父类中的实例方法
run(distance:number=10){
console.log(``跑了${distance}米远)
}
}
//实例化父类对象
const ani:Animal = new Animal("动物")
ani.run()
//实例化子类对象
const dog:Dog = new Dog("大黄")
dog.run()
//实例化子类对象
const pig:Pig = new Pig("八戒")
pig.run()
//父类和子类的关系:父子关系,此时,父类类型创建子类的对象
const dog1:Animal = new Dog("小黄")
dog1.run()
const pig1:Animal = new Pig("小猪")
pic1.run()
//该函数需要的参数是Animal类型的
function showRun(ani:Animal){
ani.run()
}
showRun(dog1)
showRun(pig1)
12、修饰符
修饰符(类中成员的修饰符):主要是描述类中的成员(属性,构造函数,方法的可访问性)
//定义一个类
class Person{
name:string
constructor(name:string){
this.name=name
}
eat(){
console.log()
}
}
13、TS常用
1、常用类型
1.1 交叉类型
交叉类型就是通过 & 符号,将多个类型合并为一个类型。
interface T1 {
name: string;
}
interface T2 {
age: number;
}
type T3 = T2 & T1
const a: T3 = {
name: 'xm',
age: 20,
}
1.2 联合类型
联合类型就是通过 | 符号,表示一个值可以是几种类型之一。
const a: string | number = 1
1.3 字面量类型
字面量类型就是使用一个字符串或数字或布尔类型作为变量的类型。
// 字符串
type BtnType ='default'| 'primary'| 'ghost'| 'dashed'| 'link'| 'text';
const btn:BtnType= 'primary'
// 数字
const a: 1 = 1
// 布尔
const a: true = true
1.4 字符串模板类型
字符串模板类型就是通过 ES6 的模板字符串语法,对类型进行约束。
type https = `https://${string}`
const a:https = `https://www.baidu.com`
2、运算符
2.1 非空断言运算符
非空断言运算符 ! 用于强调元素是非 null 非 undefined,告诉 Typescript 该属性会被明确的赋值。
// 你可以使用类型断言将其指定为一个更具体的类型:
const myCanvas = document.getElementById("main_canvas") as HTMLCanvasElement;
// 也可以使用尖括号语法(注意尽量不要在 .tsx 文件内使用,会产生冲突),是等价的:
const myCanvas = <HTMLCanvasElement>document.getElementById("main_canvas");
// 当你明确的知道这个值不可能是 null 或者 undefined 时才使用 !
function liveDangerously(x?: number | null) {
console.log(x!.toFixed());
}
2.2 可选运算符
可选链运算符 ?. 用于判断左侧表达式的值是否是 null 或 undefined,如果是将停止表达式运行。
const name = object?.name
2.3 空值合并运算符
空值合并运算符 ?? 用于判断左侧表达式的值是否是 null 或 undefined,如果是返回右侧的值。
const a = b ?? 0
3、操作符
3.1 keyof
keyof 用于获取某种类型的所有键,其返回值是联合类型。
const person: keyof {
name: string,
age: number
} = 'name' //const person: 'name' | 'age' = 'name'
3.2 typeof
typeof 用于获取对象或者函数的结构类型。
const a2 = {
name: 'xm',
}
type T1 = typeof a2 // { name: string }
function fn1(x: number): number {
return x * 10
}
type T2 = typeof fn1 // (x: number) => number
3.3 in
in 用于遍历联合类型。
const obj = {
name: 'xm',
age: 20,
sex:'男'
}
type T5 = {
[P in keyof typeof obj]: string
}
/*
{ name: string, age: string, sex:string }
*/
3.4 T[K]
T[K] 用于访问索引,得到索引对应的值的联合类型。
interface I3 {
name: string,
age: number
}
type T6 = I3[keyof I3] // string | number
4、类型别名
类型别名用来给一个类型起个新名字。类型别名常用于联合类型。这里需要注意的是我们仅仅是给类型取了一个新的名字,并不是创建了一个新的类型
type Message = string | string[]
let greet = (message: Message) => {
// ...
}
5、类型断言
类型断言就是告诉浏览器我非常确定的类型。
// 尖括号 语法
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
// as 语法
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
6、类型缩小
我们可以通过某些操作将变量的类型由一个较为宽泛的集合缩小到相对较小、较明确的集合
let func = (anything: string | number) => {
if (typeof anything === 'string') {
return anything; // 类型是 string
} else {
return anything; // 类型是 number
}
};
7、泛型
泛型就是通过给类型传参,得到一个更加通用的类型,就像给函数传参一样。
如下我们得到一个通用的泛型类型 T1,通过传参,可以得到 T2 类型 string[]、T3 类型 number[]; T 是变量,我们可以用任意其他变量名代替他。
type T1<T> = T[]
type T2 = T1<string> // string[]
type T3 = T1<number> // number[]
7.1 常见泛型定义
T:表示第一个参数
K: 表示对象的键类型
V:表示对象的值类型
E:表示元素类型
7.2 泛型接口
泛型接口和上述示例类似,为接口类型传参:
interface I1<T, U> {
name: T;
age: U;
}
type I2 = I1<string, number>
7.3 泛型约束
Typescript 通过 extends 实现类型约束。让传入值满足特定条件;
interface IWithLength {
length:number
}
function echoWithLength<T extends IWithLength>(arg:T):T{
console.log(arg.length)
return arg
}
echoWithLength('str')
通过 extends 约束了 K 必须是 T 的 key。
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
let tsInfo = {
name: "Typescript",
supersetOf: "Javascript",
difficulty: Difficulty.Intermediate
}
let difficulty: Difficulty =
getProperty(tsInfo, 'difficulty'); // OK
let supersetOf: string =
getProperty(tsInfo, 'superset_of'); // Error
7.4 泛型参数默认值
泛型参数默认值,和函数参数默认值一样,在没有传参时,给定默认值。
interface I4<T = string> {
name: T;
}
const S1: I4 = { name: '123' } // 默认 name: string类型
const S2: I4<number> = { name: 123 }
7.5 泛型工具类型
7.5.1 typeof
typeof 的主要用途是在类型上下文中获取变量或者属性的类型;
interface Person {
name: string;
age: number;
}
const sem: Person = { name: "semlinker", age: 30 };
type Sem = typeof sem; // type Sem = Person
// 使用Sem类型
const lolo: Sem = { name: "lolo", age: 5 }
7.5.2 keyof
keyof 可以用于获取某种类型的所有键,其返回类型是联合类型。
interface Person {
name: string;
age: number;
}
type K1 = keyof Person; // "name" | "age"
type K2 = keyof Person[]; // "length" | "toString" | "pop" | "push" | "concat" | "join"
type K3 = keyof { [x: string]: Person }; // string | number
7.5.3 映射类型
映射类型,它是一种泛型类型,可用于把原有的对象类型映射成新的对象类型。
interface TestInterface{
name:string,
age:number
}
// 我们可以通过+/-来指定添加还是删除
// 把上面定义的接口里面的属性全部变成可选
type OptionalTestInterface<T> = {
[p in keyof T]+?:T[p]
}
// 再加上只读
type OptionalTestInterface<T> = {
+readonly [p in keyof T]+?:T[p]
}
type newTestInterface = OptionalTestInterface<TestInterface>
7.5.4 Partial
Partial 将类型的属性变成可选
type Partial<T> = {
[P in keyof T]?: T[P]
}
type T1 = Partial<{
name: string,
}>
const a: T1 = {} // 没有name属性也不会报错
7.5.5 Required
Required 将泛型的所有属性变为必选。
// 语法-?,是把?可选属性减去的意思
type Required<T> = {
[P in keyof T]-?: T[P]
}
type T2 = Required<{
name?: string,
}>
// ts报错,类型 "{}" 中缺少属性 "name",但类型 "Required<{ name?: string | undefined; }>" 中需要该属性。ts(2741)
const b: T2 = {}
7.5.6 Readonly
Readonly 将泛型的所有属性变为只读。
type T3 = Readonly<{
name: string,
}>
const c: T3 = {
name: 'tj',
}
c.name = 'tj1' // ts 报错,无法分配到 "name" ,因为它是只读属性。ts(2540)
7.5.7 Pick
Pick 从类型中选择一下属性,生成一个新类型。
// 例子一
type Pick<T, K extends keyof T> = {
[P in K]: T[P]
}
type T4 = Pick<
{
name: string,
age: number,
},
'name'
>
/*
这是一个新类型,T4={name: string}
*/
const d: T4 = {
name: 'tj',
}
// 例子二
interface Todo {
title: string;
description: string;
completed: boolean;
}
type TodoPreview = Pick<Todo, "title" | "completed">;
const todo: TodoPreview = {
title: "Clean room",
completed: false,
};
7.5.8 Record
Record 将 key 和 value 转化为 T 类型。
// 例子一
type Record<K extends keyof any, T> = {
[key in K]: T
}
const e: Record<string, string> = {
name: 'tj',
}
const f: Record<string, number> = {
age: 11,
}
// 例子二
interface PageInfo {
title: string;
}
type Page = "home" | "about" | "contact";
const x: Record<Page, PageInfo> = {
about: { title: "about" },
contact: { title: "contact" },
home: { title: "home" },
};
7.5.9 Exclude
Exclude 将某个类型中属于另一个的类型移除掉。
type Exclude<T, U> = T extends U ? never : T;
type T0 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
type T1 = Exclude<"a" | "b" | "c", "a" | "b">; // "c"
type T2 = Exclude<string | number | (() => void), Function>; // string | number
7.5.10 Extract
Extract 从 T 中提取出 U。
type Extract<T, U> = T extends U ? T : never;
type T0 = Extract<"a" | "b" | "c", "a" | "f">; // "a"
type T1 = Extract<string | number | (() => void), Function>; // () =>void
7.5.11 Omit
Omit 使用 T 类型中除了 K 类型的所有属性,来构造一个新的类型。
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
interface Todo {
title: string;
completed: boolean;
description: string;
}
type TodoPreview = Omit<Todo, "description">;
/*
{
title: string;
completed: boolean;
}
*/
7.5.12 Parameters
Parameters 的作用是用于获得函数的参数类型组成的元组类型。
funtion test(id:number, name:string){ ... }
type TestArgsType = Parameters<typeof test>;
/*
[id:number,name:string]
*/
7.5.13 ReturnType
用来得到一个函数的返回值类型。
type Func = (value: number) => string;
const foo: ReturnType<Func> = "xxx";
8、类型别名与接口区别
8.1 定义
接口:接口的作用就是为这些类型命名和为你的代码或第三方代码定义数据模型。
类型别名:type(类型别名)会给一个类型起个新名字。 type 有时和 interface 很像,但是可以作用于原始值(基本类型),联合类型,元组以及其它任何你需要手写的类型。
8.2 Objects / Functions
两者都可以用来描述对象或函数的类型,但是语法不同。
// interface
interface Point {
x: number;
y: number;
}
interface SetPoint {
(x: number, y: number): void;
}
// type
type Point = {
x: number;
y: number;
};
type SetPoint = (x: number, y: number) => void;
8.3 type定义其他类型
与接口不同,类型别名还可以用于其他类型,如基本类型(原始值)、联合类型、元组。
// primitive
type Name = string;
// object
type PartialPointX = { x: number; };
type PartialPointY = { y: number; };
// union
type PartialPoint = PartialPointX | PartialPointY;
// tuple
type Data = [number, string];
// dom
let div = document.createElement('div');
type B = typeof div;
8.4 接口可以定义多次,类型别名不可以
与类型别名不同,接口可以定义多次,会被自动合并为单个接口。
interface Point { x: number; }
interface Point { y: number; }
const point: Point = { x: 1, y: 2 };
8.5 扩展
两者都可以扩展,接口通过 extends 来实现。类型别名的扩展就是交叉类型,通过 & 来实现。
// 接口扩展接口
interface PointX {
x: number
}
interface Point extends PointX {
y: number
}
// 类型别名扩展类型别名
type PointX = {
x: number
}
type Point = PointX & {
y: number
}
// 接口扩展类型别名
type PointX = {
x: number
}
interface Point extends PointX {
y: number
}
// 类型别名扩展接口
interface PointX {
x: number
}
type Point = PointX & {
y: number
}