十分钟,轻松入门 Typescript

Typescript 的最大优点,就是 类型检查强制约束。它可以让我们在编译运行代码之前,就避免一些类型使用不当、或者没有按照规定书写代码而引起的各种错误。

类型检查器(类型注解 Flow)

记住,TypeScript 的类型检查器,一般设置于你要进行检查的事物的冒号后面。冒号后面,则是你要定义的检查器格式。

原始类型

在 ES6 以后,Javascript 的原始类型一共有 6 种。在 Typescript 中,原始类型也是 6 种,它们分别是:

// 字符串类型
const a: string = "hello world";

// 数字类型
const b: number = 123;

// 布尔类型
const c: boolean = true;

// 空类型
const d: null = null;

// undefined 类型
const e: void = undefined;

// symbol 类型
const f: symbol = Symbol();

引用类型

数组类型

在 Typescript 中,定义数组我们有 3 种方式。分别是:

// Array<类型>,尖括号单一类型定义
const arr1: Array<number> = [1, 2, 3];

// 类型[],中括号单一类型定义
const arr2: number[] = [1, 2, 3];

// [类型,类型,类型],中括号多类型定义
const arr3: [string, number, number] = ["1", 2, 3];
对象类型

在 Typescript 中,定义对象的方式有多种,例如:

// object 类型检查器(支持所有“对象”)
const person: object = function(){}

// 大括号 类型检查器(支持特定类型)
const person: { 
  name: string; 
  age?: number // ? 代表该字段为可选,在对象中不一定要包含此属性
} = { 
  name: "steve",
  age: 20 
};

如果我们不想使用这么繁琐的 对象类型 定义方式,我们还可以使用 Type 来定义一个类型检查器:

// 定义类型检查器 Person
type Person = {
  name: string;
  age?: number; // ? 代表该字段为可选,在对象中不一定要包含此属性
};

// 在常量名 冒号 后面使用类型检查器 Person
const person:Person= {
  name: "steve",  // 该字段必须定义
  // age: 23,   // 该字段可以不定义
};
函数类型检查器
  • 定义 回调函数 类型检查器
const say = (callback: (a: string, b: number) => void) => {
  callback("1", 2);
};
say((str, number) => {
  console.log(str, number);
});
  • 定义 普通函数 类型检查器
// 参数
function square(n: number) {
  return n + 1;
}

// 返回值
function square(n: number): number {
  return n + 1;
}

// 无返回值
function say(): void {
  console.log('hello world')
}

// 这里的 rest 可以使用任意名称,目的为函数增添任意参数
const fn = (a: string, b: string, ...rest: number[]): string => {
  let str = a + b + rest;
  console.log(rest);
  console.log(str);
  return str;
};

fn("hello", "world", 1, 2, 3, 4, 5);
或类型检查器
const str: "hello" | "world" | "balala" = "balala";
type StrType = number | string | Array<any>;

断言 asserts

const nums = [1, 2, 3, 4, 5];
const res = nums.find((item) => item > 3);
// 写法一:
const num1 = res as number
// 写法二:
const num2 = <number>res    // jsx 下不能这么使用,会冲突

断言的作用是啥呢?

interface Cat {
    name: string;
    run(): void;
}
interface Fish {
    name: string;
    swim(): void;
}

function isFish(animal: Cat | Fish) {
    // 如果不写 animal as Fish,那么我们传 Cat 类型的实例到 isFish,就会报错
    if (typeof (animal as Fish).swim === 'function') {
       console.log(true);
    }
    console.log(false);
}

类型断言只能够欺骗 TS 编译器,无法避免运行时的错误,反而滥用类型断言可能会导致运行时错误。例如:

interface Cat {
    name: string;
    run(): void;
}
interface Fish {
    name: string;
    swim(): void;
}

function swim(animal: Cat | Fish) {
    (animal as Fish).swim();
}

const tom: Cat = {
    name: 'Tom',
    run() { console.log('run') }
};
swim(tom);

编译时不会报错,但在运行时会报错 Uncaught TypeError: animal.swim is not a function`,所以,不要轻易使用断言

接口类型检查器(interface)
  1. 普通 接口类型检查器 定义
   interface Person {
     name: string;
     age: number;
     sex?: string;  // 可选
     readonly summary: string;  // 关键字 readonly,可以使该属性成为只读属性
   }
   
   const person: Person = { name: "steve", age: 20, summary: "balala" };
   
   function printPerson(person: Person) {
     console.log(person.name);
     console.log(person.age);
     console.log(person.sex);
     // 报错:Cannot assign to 'summary' because it is a read-only property.
     person.summary = "lalala";  // summary 为只读属性,  
   }
  1. 动态 接口类型检查器 定义
  interface State {
    page: number; // 页码
    total: number; // 总数
    // 这里的 string,代表这个键可以定义的类型
    [key: string]: number; // 每页条数
  }

  const state:State = {
    page: 1,
    total: 100,
    pageSize: 30
  }
类 class
  1. 声明一个类
   // 定义 Person 类
   class Person {
     name: string; // this.name
     // private,无法使用实例直接访问
     private age: number;
     // protected,只能在本 class 和子类 class 中才能访问
     // readonly,在实例只能被读取,无法被赋值
     protected readonly gender: boolean;
   
     // 构造器,为 实例属性 进行赋值
     constructor(name: string, age: number) {
       this.name = name;
       this.age = age;
       this.gender = true 
     }
   
     // 定义实例方法
     sayHi(msg: string): void {
       console.log(`I am ${this.name}, ${msg}`);
     }
   }
   
   // 定义 Student 类,使用 extends 进行继承
   class Student extends Person {
     private constructor(name: string, age: number) {
       // 继承父类的 实例属性
       super(name, age);
       console.log(this.gender);
     }
   
     // 定义 静态方法,直接使用 类名 进行调用
     static create(name: string, age: number) {
       return new Student(name, age);
     }
   }
   
   const tom = new Person("tom", 18);
   console.log(tom.name);   // √ name 默认为公有属性(也可以添加 public 修饰符),可以被访问
   console.log(tom.age);    // X 报错,age 为私有属性,除了类中,无法被外界访问
   console.log(tom.gender); // X 报错,gender 为保护属性,只能在本类和子类中访问,无法被外界访问
   
   // X 报错,因为 Student 的 constructor 为 private,故不能被创建
   const jack = new Student("steve", 25);
   const jack = Student.create("jack", 18);
  1. class 的接口(interface)

定义类的接口,可以确定在类中需要定义哪些属性和方法。

   interface EatAndRun {
     eat(food: string): void;
     run(distance: number): void;
   }
   // 使用 EatAndRun 实现了 Person 类
   class Person implements EatAndRun {
     // 该类中定义了 eat 方法
     eat(food: string): void {
       console.log(`吃东西:${food}`);
     }
     // 该类中定义了 run 方法
     run(distance: number) {
       console.log(`直立行走:${distance}`);
     }
   }
   
   // 使用 EatAndRun 实现了 Animal 类
   class Animal implements EatAndRun {
     // 该类中定义了 eat 方法
     eat(food: string): void {
       console.log(`进食:${food}`);
     }
     // 该类中定义了 run 方法
     run(distance: number) {
       console.log(`爬行:${distance}`);
       return 1;
     }
   }
  1. 抽象类

使用 abstract 修饰词修饰的类,被叫做抽象类。使用 abstract 修饰词修饰的方法,叫做抽象方法。

抽象类不能直接使用(无法 new 出实例),一定要被继承,然后使用继承类。

// 定义一个动物抽象类
abstract class Animal {
  eat(food: string): void {
    console.log(`狗狗吃了一块:${food}`);
  }
  // 抽象方法在继承的时候一定要被重写
  abstract run(distance: number): void;
}
// 我们定义一个 Dog 类,继承自 Animal 抽象类
class Dog extends Animal {
  // 抽象方法,一定要被重写
  run(distance: number): void {
    console.log(`狗狗跑了${distance}公里`)
  }
}

const dog = new Dog();

dog.eat('骨头');  // "狗狗吃了一块:骨头" 
dog.run(1); // "狗狗跑了1公里" 
interface 和 type 的区别

一般来说,interface(接口) 和 type(类型别名) 可以混用,但是也存在一些差异。

1. 对象/函数

两者都可以用来描述对象的形状或函数签名,但语法不同。

interface,一般来说 name 后面直接写 花括号:

// 约束对象
interface Point {
  x: number;
  y: number;
}

// 约束函数
interface SetPoint {
  (x: number, y: number): void;
}

type,一般来说 name 后面都是接 等号:

// 约束对象
type Point = {
  x: number;
  y: number;
};

// 约束函数
type SetPoint = (x: number, y: number) => void;

2. 其他类型

与 interface 不同,type 也可用于其他类型,例如 原始类型、联合类型和数组类型。

// 原始类型
type Name = string;

// 对象类型
type PartialPointX = { x: number; };
type PartialPointY = { y: number; };

// union类型
type PartialPoint = PartialPointX | PartialPointY;

// 数组类型
type Data = [number, string];

3. 扩展

两者都可以进行扩展,但同样语法不同。此外,请注意 interface 和 type 不是相互排斥的。interface 可以扩展 type,反之亦然。

// 接口扩展接口
interface PartialPointX { x: number; }
interface Point extends PartialPointX { y: number; }

//类型别名扩展类型别名
type PartialPointX = { x: number; };
type Point = PartialPointX & { y: number; };

// 接口扩展类型别名
type PartialPointX = { x: number; };
interface Point extends PartialPointX { y: number; }

// 类型别名扩展接口
interface PartialPointX { x: number; }
type Point = PartialPointX & { y: number; };

4. 声明合并

与类型别名不同,接口可以定义多次,并将被视为单个接口(所有声明的成员都被合并)。

// These two declarations become:
// interface Point { x: number; y: number; }
interface Point { x: number; }
interface Point { y: number; }

const point: Point = { x: 1, y: 2 };

泛型

什么是泛型?

当我们在使用函数之前,不知道函数内部具体需要什么类型的 数据 的时候,可以在 使用的时候 动态设置。泛型,就是这样的一种写法。

// 在使用之前不知道函数内部需要什么类型,可以在使用的时候动态设置
function createArray<T>(length: number, value: T): T[] {
  const arr = Array<T>(length).fill(value);
  return arr;
}

// 我们在这里进行函数调用,调用的时候就对传参类型进行了设置
const res = createArray<string>(10, 'hello');
console.log(res);

枚举类型

枚举可以实现 数字类型自增长

定义枚举类型:

enum En {
  a = 3,    // 3
  b,        // 4
  c,        // 5
}

// 获取枚举值
En.a        // 3
En['a']     // 3

// 根据枚举值,获取枚举键
En[3]       // a,值为 3 的 enum 元素 name

在枚举中,除开数字之外的其他任何类型都无法实现 自增长,一定要全部定义,不然 就会报错

enum En{
  a = 'say',    // say
  b = 'hello',  // hello
  c = 'world',  // world,这里一定要定义
}

En.a        // say
En['a']     // say
En['say']   // undefined
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,383评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,522评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,852评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,621评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,741评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,929评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,076评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,803评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,265评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,582评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,716评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,395评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,039评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,027评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,488评论 2 361
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,612评论 2 350

推荐阅读更多精彩内容