TypeScript 学习笔记(基础篇)

什么是 TypeScript

定义:添加了类型系统的 JavaScript,适用于任何规模的项目。

JavaScript 的类型分为两种:原始数据类型对象类型
原始数据类型包括:布尔值、数值、字符串、null、undefined 以及 ES6 中的新类型 Symbol 和 ES10 中的新类型 BigInt。

变量声明

let 变量名:类型 = 值;
var 变量名 = 值; //声明变量并初始值,但不设置类型,该变量可以是任意类型:如果只声明,没有值则默认初始值为 undefined:

布尔类型:let isDone: boolean = false;
数值 let decLiteral: number = 6;
字符串 let name: string = "bob";
空值 Void类型(声明一个void类型的变量没有什么大用,因为你只能为它赋予undefined和null)
let unusable: void = undefined;
Null 和 Undefined类型( 和 void相似,它们的本身的类型用处不是很大)
let u: undefined = undefined; let n: null = null;
注意:默认情况下null和undefined是所有类型的子类型。 就是说你可以把 null和undefined赋值给number类型的变量。然而,当你指定了--strictNullChecks标记,null和undefined只能赋值给void和它们各自。 可以用 | 来支持多种类型,如下:
// 启用 --strictNullChecks
let x: number | null | undefined;
x = 1; // 编译正确
x = undefined; // 编译正确
x = null; // 编译正确
Any类型
let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; //可以随意修改类型
let list: any[] = [1, true, "free"]; (定义一个数组,包含了不同的类型的数据)
在任意值上访问任何属性都是允许的:
let anyThing: any = 'hello';
console.log(anyThing.myName);
声明一个变量为任意值之后,对它的任何操作,返回的内容的类型都是任意值。
变量如果在声明的时候,未指定其类型,那么它会被识别为任意值类型
let myFavoriteNumber; //类型未指定 则默认为any类型 所以可随意修改
myFavoriteNumber = 'seven'; myFavoriteNumber = 7;
Never类型
never 是其它类型(包括 null 和 undefined)的子类型,代表从不会出现的值。
let x: never;
let y: number;
x = 123; // 编译错误,数字类型不能转为 never 类型
x = (()=>{ throw new Error('exception')})(); // 运行正确,never 类型可以赋值给 never类型
y = (()=>{ throw new Error('exception')})(); // 运行正确,never 类型可以赋值给 数字类型
// 返回值为 never 的函数可以是抛出异常的情况
function error(message: string): never {
throw new Error(message);
}
// 返回值为 never 的函数可以是无法被执行到的终止点的情况
function loop(): never {
while (true) {}
}

类型推断

let myFavoriteNumber = 'seven'; (声明赋值后会有类型推断,只声明则默认为any类型)
myFavoriteNumber = 7; //报错 在没有明确的指定类型的时候推测出一个类型 此处为number类型

联合类型

联合类型(Union Types)表示取值可以为多种类型中的一种。使用 | 分隔每个类型

var val:string|number 
val = 12 
console.log("数字为 "+ val) 
val = "Runoob" 
console.log("字符串为 " + val)

我们只能访问此联合类型的所有类型里共有的属性或方法,否则会报错
联合类型的变量在被赋值的时候,会根据类型推论的规则推断出一个类型

let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';
console.log(myFavoriteNumber.length); // 5
myFavoriteNumber = 7;
console.log(myFavoriteNumber.length); // 编译时报错

对象类型

在 TypeScript 中,我们使用接口(Interfaces)来定义对象的类型。

interface Person {
    name: string;
    age: number;
}

let tom: Person = {
    name: 'Tom',
    age: 25
};

我们定义了一个接口 Person,接着定义了一个变量 tom,它的类型是 Person。这样,我们就约束了 tom 的形状必须和接口 Person 一致。
其中,定义的变量比接口多或少了一些属性都是不允许的
有时我们希望不要完全匹配一个形状,那么可以用可选属性:?表示

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

let tom: Person = {
    id: 89757,
    name: 'Tom',
};
tom.id = 9527; //报错 属性值为只读

//其返回值为表达式的情况
interface RunOptions { 
    program:string; 
    commandline:string[]|string|(()=>string);  //可以是string数组,string,或者返回string类型的表达式
} 
let  options:RunOptions = {program:"test1",commandline:()=>{return "Hello World";}}; 

索引签名(Index Signatures)
有时您不知道类型属性的所有名称,但您确知道值的形状。在这些情况下,可以使用索引签名来描述可能值的类型。其语法为:
{ [key: KeyType]: ValueType },比如:

interface StringByString {
  [key: string]: string; //属性名为string类型的,值也为string类型的
}
const heroesInBooks: StringByString = {
  'Gunslinger': 'The Dark Tower',
  'Jack Torrance': 'The Shining'
};

其中 key可以用任何名称,KeyType是键的类型,可以是string,number或symbol,不允许使用其他类型。ValueType键的值,可以是string, number, or boolean 这三种类型。一旦定义了索引签名,对象里面的属性都要符合该规则,比如:

interface NumberDictionary {
  [index: string]: number, //定义索引签名index,其键的类型为string,其值的类型为number
  length: number; // 属性值要求为number类型,length为number,符合规则,ok
  name: string; //属性值要求为number类型,name属性值为string,不符合 报错
}

可以将 [index: string]: number修改为[index: string]: number|string 即可,兼容两种类型的值。
如果不太理解,可以参考这些文档:
https://www.typescriptlang.org/docs/handbook/2/objects.html#index-signatures
https://bbs.huaweicloud.com/blogs/306598

数组类型

最简单方式:类型[] 如:let list: number[] = [1, 2, 3]; 此时数组项中不允许出现其他的类型
数组泛型:Array<elemType> 如:let fibonacci: Array<number> = [1, 1, 2, 3, 5];
需要注意:类数组不是数组类型,比如 arguments,但可以使用接口形式表示

function sum() {
    let args: {
        [index: number]: number;
        length: number;
        callee: Function;
    } = arguments;
    //这里补充下,因为索引签名index的类型为number,所以只会检查属性名为number类型的,这里的length、callee都为字符串,所以会自动跳过
    //如果改成 [index: string]: number,则会检查length和callee,callee会因为值类型为Function编译错误
}

any 在数组中的应用 let list: any[] = ['xcatliu', 25, { website: 'http://xcatliu.com' }];

元组(已知元素数量和类型的数组)
let x: [string, number] = ['hello', 10];

函数类型

在 JavaScript 中,有两种常见的定义函数的方式——函数声明和函数表达式
函数声明

//输入参数和输出返回值 都需要类型定义
function sum(x: number, y: number): number {
    return x + y;
}

函数表达式
在 TypeScript 的类型定义中,=> 用来表示函数的定义,左边是输入类型,需要用括号括起来,右边是输出类型。

//写起来比较长,可以使用下面接口形式定义函数形状
let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
    return x + y;
};

用接口定义函数的形状

interface SearchFunc {
    (source: string, subString: string): boolean;
}

let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
    return source.search(subString) !== -1;
}

与接口中的可选属性类似,我们用 ? 表示可选的参数,可选参数后面不允许再出现必需参数了,如果可选参数有默认值,可允许放在前面。

//默认值
function buildName(firstName: string = 'Tom', lastName: string) {
    return firstName + ' ' + lastName;
}
let tomcat = buildName('Tom', 'Cat');
let cat = buildName(undefined, 'Cat');

//rest剩余参数
function push(array: any[], ...items: any[]) {
    items.forEach(function(item) {
        array.push(item);
    });
}

let a = [];
push(a, 1, 2, 3);

//综合使用(包含默认值、可选参数、rest参数等)
function buildName(firstName: string,  rate:number = 0.50, ...restOfName: string[],lastName?: string) :string {
    if (lastName)
        return firstName + " " + lastName;
    else
        return firstName;
}

函数重载:允许一个函数接受不同数量或类型的参数时,作出不同的处理。如:

function reverse(x: number): number;
function reverse(x: string): string;
function reverse(x: number | string): number | string | void {
    if (typeof x === 'number') {
        return Number(x.toString().split('').reverse().join(''));
    } else if (typeof x === 'string') {
        return x.split('').reverse().join('');
    }
}

class Car  {
    engine:string;    // 字段 
    static num:number;  //静态属性 可以直接通过类名调用。
    // 构造函数 
    constructor(engine:string) { 
        this.engine = engine 
    }  
    // 方法 
    disp():void { 
        console.log("发动机为 :   "+this.engine) 
    } 
}
var obj:Car = new Car("XXSY1") // 创建一个对象
console.log("读取发动机型号 :  "+obj.engine)  // 访问字段
obj.disp() // 访问方法

会编译成

var Car = /** @class */ (function () {
    // 构造函数 
    function Car(engine) {
        this.engine = engine;
    }
    // 方法 
    Car.prototype.disp = function () {
        console.log("发动机为 :   " + this.engine);
    };
    return Car;
}());

TypeScript 可以使用三种访问修饰符(Access Modifiers),分别是 public、private(私有,子类不可访问) 和 protected(子类可访问)。
TypeScript 编译之后的代码中,并没有限制 private 属性在外部的可访问性,也就是编译时会限制,实际允许并不会限制
当构造函数修饰为 private 时,该类不允许被继承或者实例化,当构造函数修饰为 protected 时,该类只允许被继承。
抽象类:只能被继承,不允许实例化,其抽象方法必须在子类中被实现。比如:

//定义抽象类
abstract class Animal {
  public name;
  public constructor(name) {
    this.name = name;
  }
  //抽象方法
  public abstract sayHi();
}
//Cat 继承Animal 抽象类 其抽象方法sayHi()需要被实现
class Cat extends Animal {
  public sayHi() {
    console.log(`Meow, My name is ${this.name}`); //子类实现父类抽象类的sayHi方法
  }
}

let cat = new Cat('Tom');

类与接口的扩展

一个类可以实现一个或多个接口

//定义Alarm和Light接口
interface Alarm {
    alert(): void;
}
interface Light {
    lightOn(): void;
    lightOff(): void;
}

//定义类
class Door {
}

//类继承extends,并实现implements接口
class SecurityDoor extends Door implements Alarm {
    alert() {
        console.log('SecurityDoor alert');
    }
}

//类实现多个接口
class Car implements Alarm,Light {
    alert() {
        console.log('Car alert');
    }
   lightOn() {
        console.log('Car light on');
    }
    lightOff() {
        console.log('Car light off');
    }
}

接口与接口之间可以是继承关系

interface Alarm {
    alert(): void;
}
//LightableAlarm接口 继承 Alarm接口
interface LightableAlarm extends Alarm {
    lightOn(): void;
    lightOff(): void;
}

常见的面向对象语言中,接口是不能继承类的,但是在 TypeScript 中却是可以的,接口也是可以继承类的

class Point {
    /** 静态属性,坐标系原点 */
    static origin = new Point(0, 0);
    /** 静态方法,计算与原点距离 */
    static distanceToOrigin(p: Point) {
        return Math.sqrt(p.x * p.x + p.y * p.y);
    }
    /** 实例属性,x 轴的值 */
    x: number;
    /** 实例属性,y 轴的值 */
    y: number;
    /** 构造函数 */
    constructor(x: number, y: number) {
        this.x = x;
        this.y = y;
    }
    /** 实例方法,打印此点 */
    printPoint() {
        console.log(this.x, this.y);
    }
}

interface Point3d extends Point {
    z: number;
}

let point3d: Point3d = {x: 1, y: 2, z: 3};

实际上,当我们在声明 class Point 时,除了会创建一个名为 Point 的类之外,同时也创建了一个名为 Point 的类型(实例的类型),其中实例类型不含构造函数、静态属性或静态方法
所以,Point的实例类型为:

interface PointInstanceType {
    x: number;
    y: number;
    printPoint(): void;
}

let p1: Point; //定义变量p1为Point类型
let p2: PointInstanceType; //定义变量p2为 PointInstanceType类型
其类型 Point 和类型 PointInstanceType 是等价的

// 等价于 interface Point3d extends PointInstanceType
interface Point3d extends Point {
    z: number;
}

相关文档参考:http://ts.xcatliu.com/introduction/what-is-typescript.html

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

推荐阅读更多精彩内容