typescript是什么
- Typescript是由微软开发的一款开源的编程语言
- Typescript是Javascript的超集,遵循最新的ES5/ES6规范。TypeScript扩展了Javascript语法
- TS提供的类型系统可以帮助我们在写代码的时候提供更丰富的语法提示
- 在创建前的编译阶段经过类型系统的检查,就可以避免很多线上的错误
TypeScript安装和编译
安装
npm i typescript -g
复制代码
安装完后,控制台会多一个 tsc
命令,可以使用 tsc 文件名
的格式编译 ts
文件
初次编译
Typescript 文件一般结尾使用 .ts
结尾
创建一个 helloworld.ts
文件
// helloworld.ts
var a = 1;
复制代码
编译 helloworld.ts
文件,在命令行中输入
tsc helloworld.ts
复制代码
此时在同级目录下会得到一个 helloworld.js
文件,这个文件就是通过 Typescript 编译来的
在 Vscode 中快速编译 Typescript
Vscode 提供一个监听功能,不用每次修改都使用命令行编译
生成配置文件
tsc --init
复制代码
文件目录下会生成一个 tsconfig.json
配置文件。文件中提供了很多 ts
编译相关配置。比如编译后的js版本、编译后输出位置等等
开启监听
Vscode菜单 => 终端 => 运行任务... => tsc监视 - tsconfig.json
mac快捷键: cmd + shift + B
Typescript 语法
数据类型
变量声明的一般格式
// var 变量名: 数据类型 = 值
var a: boolean = true
// var 变量名: 数据类型1 | 数据类型2 = 值
var a: boolean | undefined = true
a = undefined
复制代码
通过这种方式限制变量的类型,如果类型错误系统会给出相应的提示。
原始类型
js 中原始类型分为:boolean、number、string、null、undefined、symbol
/**
* 数据类型
* boolean 布尔类型
* number 数字类型
* string 字符串类型
* unll
* undefined
*/
let married: boolean = false;
let age: number = 10;
let site: string = 'beijin'
let nation: string | null | undefined = '汉'
复制代码
数组类型 array
let arr: number[] = [1, 2, 3]
let arr2: Array<number> = [1, 2, 3]
复制代码
表示对象 (不是 Object 类型)
- 限制每一次属性的类型
- 可有可无的属性用 ?
- 如果有无法预知的属性,用 [propName: string]
- 分割可以使用
\n
、,
、;
let identity:{
name: string,
age: number,
nation?: string,
[propName: string]: any
} = {name: '张三', age: 16, ddd: 123}
复制代码
元组类型 tuple
元组( Tuple )表示一个已知数量和类型的数组
let tupleArr: [string, number] = ['张', 5];
复制代码
枚举类型 enum
事先考虑某一个变量的所有的可能的值。默认情况下,从0开始为元素编号,当然也可以手动赋值
普通枚举
enum Gender {
BOY,
GIRL
}
console.log(`李雷是${Gender.BOY}`); // 0
console.log(`韩梅梅是${Gender.GIRL}`); // 1
enum Week {
MONDAY = 1,
TUESDAY = 2
}
console.log(`今天是星期${Week.MONDAY}`);
复制代码
常数枚举
- 常数枚举与普通枚举的区别是,它会在编译阶段被删除,并且不能包含计算成员。
- 假如包含了计算成员,则会在编译阶段报错
const enum Colors {
Red,
Yellow,
Blue
}
const enum Color {Red, Yellow, Blue = "blue".length}; // 报错
复制代码
任意类型 any
any就是可以赋值给任意类型, 使用 any 相当于放弃了 ts 类型检查,慎用!
- 第三方库没有提供类型文件时可以使用any
- 类型转换遇到困难时
- 数据结构太复杂难以定义
let root:any = document.getElementById('root');
root.style.color = 'red';
复制代码
void 类型
void 表示没有任何类型
当一个函数没有返回值时,TS 会认为它的返回值是 void 类型
function fn():void {
}
复制代码
never 类型
never类型表示的是那些永不存在的值的类型。 例如, never类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型; 变量也可能是 never类型,当它们被永不为真的类型保护所约束时。
// 返回never的函数必须存在无法达到的终点
function error(message: string): never {
throw new Error(message);
}
// 推断的返回值类型为never
function fail() {
return error("Something failed");
}
// 返回never的函数必须存在无法达到的终点
function infiniteLoop(): never {
while (true) {
}
}
复制代码
any void never 比较
- any:任何值
- void:不能有任何值
- 一个没有 return 值 或者 return 值是 undefined 的方法
- never:永远不会有返回值
- 函数不会有执行结果
- 函数报错,没有执行结果
- 函数是死循环,没有执行结果
- 函数不会有执行结果
Object 类型
object表示非原始类型,也就是除number,string,boolean,symbol,null或undefined之外的类型。
函数
函数的定义
形参和实参必须完全一样。比如形参有3个值,实参传2个值就会报错
function hello(name:string):void {
console.log('hello',name);
}
hello('张三');
复制代码
定义函数类型
定义一类函数类型,复用类型判断
type GetUsernameFunction = (x:string,y:string)=>string;
let getUsername:GetUsernameFunction = function(firstName,lastName){
return firstName + lastName;
}
复制代码
可选参数、默认参数、剩余参数
- 可选参数:必须跟在必须参数后。
- 默认参数:用 = 号链接。
- 剩余参数:把参数收集到一个变量里
// 可选参数
function buildName(firstName: string, lastName?: string) {
// ...
}
// 默认参数
function buildName2(firstName: string, lastName: string = 'sss') {
// ...
}
// 剩余参数
function buildName(firstName: string, ...restOfName: string[]) {
return firstName + " " + restOfName.join(" ");
}
let employeeName = buildName("Joseph", "Samuel", "Lucas", "MacKinzie");
复制代码
函数重载
根据函数接受的参数不同,做不同的处理
let obj: any = {};
function attr(val: string): void;
function attr(val: number): void;
function attr(val: any): void {
if (typeof val === 'number') {
obj.age = val;
} else if (typeof val === 'string') {
obj.name = val;
}
}
attr('zfpx');
attr(9);
attr(true); // 报错 没有定义 boolean 类型
console.log(obj);
复制代码
注意:function attr(val:any): void 并不是重载列表的一部分,只是写法要求
类
如何定义类
class Greeter {
// 实例的属性
greeting: string;
// 构造器
constructor(message: string) {
this.greeting = message;
}
// 原形方法
greet(): string {
return "Hello, " + this.greeting;
}
// 存取器(方法在原形上,赋值的属性在实例上)
get greetName() {
return this.greeting
}
set greetName(message: string) {
this.greeting = message
}
}
let greeter = new Greeter("world");
复制代码
继承
- 子类继承父类后子类的实例就拥有了父类中的属性和方法,可以增强代码的可复用性
- 将子类公用的方法抽象出来放在父类中,自己的特殊逻辑放在子类中重写父类的逻辑
- super可以调用父类上的方法和属性
class Animal {
name: string;
constructor(theName: string) {
this.name = theName;
}
move(distanceInMeters: number = 0) {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}
class Snake extends Animal {
constructor(name: string) {
super(name);
}
move(distanceInMeters = 5) {
console.log("Slithering...");
super.move(distanceInMeters);
}
}
let sam = new Snake("Sammy the Python");
sam.move();
复制代码
修饰符 public protected private readonly
修饰符默认为 public
- public 类里面、子类、其它任何地方外边都可以访问
- protected 类里面、子类、都可以访问,其它任何地方不能访问
- private 类里面可以访问, 子类和其它任何地方都不可以访问
- readonly 只读 只读属性必须在声明时或构造函数里被初始化
class Father {
public name: string; //类里面 子类 其它任何地方外边都可以访问
protected age: number; //类里面 子类 都可以访问,其它任何地方不能访问
private money: number; //类里面可以访问, 子类和其它任何地方都不可以访问
constructor(name: string, age: number, money: number) {//构造函数
this.name = name;
this.age = age;
this.money = money;
}
desc() {
console.log(`${this.name} ${this.age} ${this.money}`);
}
}
class Child extends Father{
constructor(name: string, age: number, money: number) {
super(name, age, money);
}
getName() { console.log(this.name) }
getAge() { console.log(this.age) }
getMoney() { console.log(this.money) } // 报错,不能读取
}
let child = new Child('zfpx',10,1000);
console.log(child.name);
console.log(child.age); // 报错,不能读取
console.log(child.money); // 报错,不能读取
复制代码
静态属性 静态方法 static
在类上添加属性和方法
class Father {
static className = 'Father';
static getClassName() {
return Father.className;
}
public name: string;
constructor(name:string) {//构造函数
this.name=name;
}
}
console.log(Father.className);
console.log(Father.getClassName());
复制代码
抽象类 abstract
对子类的限制,限制子类必须实现某些功能
- 抽象描述一种抽象的概念,无法被实例化,只能被继承
- 无法创建抽象类的实例
- 抽象方法不能在抽象类中实现,只能在抽象类的具体子类中实现,而且必须实现
abstract class Animal3 {
abstract speak(): void;
}
class Cat extends Animal3{
speak(){ // 子类必须实现
console.log('喵喵喵');
}
}
let Animal = new Animal(); // 报错,抽象类不能实例
let cat = new Cat();
cat.speak();
复制代码
接口 interface
复用约束
简单的声明接口
// 约束对象
interface userInterface {
name: string,
height: number,
}
function user(obj: userInterface):void {
console.log(`${obj.name}: ${obj.height}`)
}
user({name: '张三', height: 170})
复制代码
函数类型接口
对方法传入的参数和返回值进行约束
// 约束函数
interface discount {
(price: number): number
}
let cost: discount = function (price: number): number {
return price * 8
}
cost(100)
复制代码
可索引接口
对数组和对象进行约束
- [index: number] 约束数组
- [index: string] 约束对象
// 约束数组
interface arrInterface {
[index: number]: string,
}
let arr: arrInterface = ['123', '333']
// 约束对象
interface objInterface {
[index: string]: string,
}
let obj: objInterface = {name: 'ddd'}
复制代码
类接口
限制类的属性、方法、参数、返回值
- 一个类可以有多个接口
- 接口检查的是实例部分,静态部分将不会检查
interface Speakable { // 第一个接口
name: string;
speak(words: string): void
}
interface Bird { // 第二个接口
fly(): void
}
class Dog implements Speakable, Bird {
name: string;
constructor(name: string) {
this.name = name
}
speak(words: string): void {
console.log(words);
}
fly(): void {}
}
let dog = new Dog('旺财');
dog.speak('汪汪汪');
复制代码
构造函数的类型
约束构造函数的参数、实例上的属性方法
// 类接口
interface ClockInterface {
tick(): void;
}
// 构造函数接口
interface ClockConstructor {
new (hour: number, minute: number): ClockInterface;
}
// 实现类
class DigitalClock implements ClockInterface {
constructor(h: number, m: number) { }
tick() {
console.log("beep beep");
}
}
// 使用构造函数接口,接受 ClockConstructor 构造函数,并返回 ClockInterface
function createClock(ctor: ClockConstructor, hour: number, minute: number): ClockInterface {
return new ctor(hour, minute);
}
let digital = createClock(DigitalClock, 12, 17);
复制代码
接口的继承
接口可以通过继承扩展子接口
interface aaa {
name: string
}
interface bbb extends aaa {
age: number
}
let u: bbb = {
name: 'sss',
age: 123
}
复制代码
readonly 设置接口只读
当不希望接口上的属性被修改时,使用 readonly
interface Person{
readonly id: number;
name: string
}
let tom: Person = {
id: 1,
name: 'zhufeng'
}
tom.id = 1; // 报错。只读属性不能被修改
复制代码
泛型
- 泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性
- 泛型
T
作用域只限于函数内部使用
简单的泛型
如果有一个函数,接受一个变量,并要求返回值与变量的类型一样
// 通用
function identity<T>(arg: T): T { // `T` 只表示类型,不表示值
return arg;
}
// 数组
function loggingIdentity<T>(arg: Array<T>): Array<T> {
return arg;
}
复制代码
类的泛型
class MyArray <T> {
private list: T[] = [];
add(value:T) {
this.list.push(value);
}
}
let arr = new MyArray();
arr.add(1)
arr.add('1')
let arr2 = new MyArray<string>(); // 固定泛型类型
arr2.add(1) // 报错
arr2.add('1')
复制代码
类型推论
在有些没有明确指出类型的地方,类型推论会帮助提供类型
- 定义时未赋值就会推论成any类型
- 如果定义的时候就赋值就能利用到类型推论
let x = 1 // 默认为 number
x = true // 报错
let y // 默认为 any
y = 1
y = true
复制代码
包装对象(Wrapper Object)
基础数据类型用字面量声明、构造函数声明出来的是不一样的。原始数据类型(字面量)调用方法时,JavaScript 会在原始数据类型和对象类型之间做一个迅速的强制性切换。
ts 中应该避免构造函数声明
let isOK: boolean = true; // 编译通过
let isOK: boolean = Boolean(1) // 编译通过
let isOK: boolean = new Boolean(1); // 编译失败 期望的 isOK 是一个原始数据类型
复制代码
类型断言
尖括号语法
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;
复制代码
- 类型断言可以将一个联合类型的变量,指定为一个更加具体的类型
- 不能将联合类型断言为不存在的类型
let name5: string | number;
(name5 as number).toFixed(3);
(name5 as string).length;
(name5 as boolean);
复制代码