typescript学习笔记

重点:

  1. 解构与指示类型有时很像
  2. 关于类的部分
  3. 有些时候会有语法错误,比如不能这样或那样调用。这其实都是ts的语法要求。我们发现,其实将所谓的错误代码编译成js后,是可以正常运行的。

数组类型:

let list: number[] = [1, 2, 3];

let list: Array<number> = [1, 2, 3];

元组 Tuple:数量、类型已知的 数组

let x: [string, number];

枚举类型:

enum Color {Red = 1, Green = 2, Blue = 4}
let c: Color = Color.Green;

void、null、undefined类型

object表示非原始类型,也就是除number,string,boolean,symbol,null或undefined之外的类型。注意什么是原始类型、什么是非原始类型

类型断言,好比类型转换

使用尖括号 let strLength: number = (<string>someValue).length;
使用as let strLength: number = (someValue as string).length;

解构

https://www.tslang.cn/docs/handbook/variable-declarations.html
[first, second]: [number, number]
给属性以不同的名字 let { a: newName1, b: newName2 } = o;
令人困惑的是,这里的冒号不是指示类型的。 如果你想指定它的类型, 仍然需要在其后写上完整的模式。
let {a, b}: {a: string, b: number} = o;
对上面令人困惑的地方,我自己的理解及总结:
类似于let {a,b}=o的这种表达式,我们先从大的方面确定,这是一个解构表达式;
然后,对于解构的属性的类型,不能在其大括号内声明,只能在其大括号内设置别名;
若要进行类型声明,必须在其大括号外面。

默认值

let { a, b = 1001 } = wholeObject;

函数声明+解构+默认值

function f({ a, b = 0 } = { a: "" }): void {
 // ...
}

表示函数接收一个参数,可以将其解构成{a,b},如果传入的参数没有b这个属性,则使用属性b的默认值0。如果没传参数,则使用参数的默认值{ a: "" }

展开,符号为... 注意:展开仅包含对象 自身的可枚举属性

let first = [1, 2];
let second = [3, 4];
let bothPlus = [0, ...first, ...second, 5];

let defaults = { food: "spicy", price: "$$", ambiance: "noisy" };
let search = { ...defaults, food: "rich" };

接口的作用

为你代码中对象的结构类型命名
接口其实就是对一组规则的命名
不使用接口定义时的类型声明

function printLabel(labelledObj: { label: string }) {
 console.log(labelledObj.label);
}

只读属性,关键字readonly
在接口中如何使用

interface Point {
 readonly x: number;
 readonly y: number;
}
let p1: Point = { x: 10, y: 20 };

是不是也可以这样用,不定义接口

let p1:{readonly x: number,readonly y: number} = { x: 10, y: 20 };

索引签名。TypeScript支持两种索引签名:字符串和数字。

interface SquareConfig {
 color?: string;
 width?: number;
 [propName: string]: any;// 这就是字符串索引签名
}

举一个数字索引签名的例子

interface StringArray {
 [index: number]: string;
}
let myArray: StringArray;
myArray = ["Bob", "Fred"];

函数类型,就是描述“某个方法应该长成什么样子”

定义

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

使用

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

感想
可能只对声明一个函数变量然后赋值的这种情况有意义;对于ionic中的那种方法声明好像就没有意义了。

类类型,就是规定类应该长成什么样子

其实和java中的某个类实现某个接口没什么区别
看例子

interface ClockInterface {
 currentTime: Date;
 setTime(d: Date);
}
class Clock implements ClockInterface {
 currentTime: Date;
 setTime(d: Date) {
 this.currentTime = d;
 }
 constructor(h: number, m: number) { }
}

实例类型

let greeter: Greeter,意思是 Greeter类的实例的类型是 Greeter
备注:这里的Greeter是一个类

混合类型

interface Counter {
 (start: number): string;// 表示这是一个方法
 interval: number;// 表示该方法还有一个属性
 reset(): void;// 表示该方法还有一个方法属性
}

使用

function getCounter(): Counter {
 let counter = <Counter>function (start: number) { };
 counter.interval = 123;
 counter.reset = function () { };
 return counter;
}
let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;

接口继承类

这是一个类

class Control {
 private state: any;
}

以下接口继承了上面这个类,并定义了一个方法

interface SelectableControl extends Control {
 select(): void;
}

这个继承关系会将Control类的所有公有及私有属性和方法都继承过来,但不包括其具体实现(接口只可能有定义,不可能有实现)
因为接口可以继承类的私有成员,所以只有Control或Control的子类可以实现该接口。
错误实例

// 错误:“Image”类型缺少“state”属性。
class Image implements SelectableControl {
 select() { }
}

类是具有两个类型的:静态部分的类型和实例的类型。

当一个类实现了一个接口时,只对其实例部分进行类型检查。 constructor存在于类的静态部分,所以不在检查的范围内。

关于类

  1. Private定义的属性或方法只能在本类中使用,protected定义的属性或方法可以在本类及子类中使用。它们统统都不能在类之外或者说在实例中使用
  2. 如果不想让一个类被直接调用其构造方法将其实例化,可以将其构造方法定义为protect的或者private的
  3. 在类中使用存取器,也就是使用getter和setter,其目的就是在对属性存取值的时候,可以加上逻辑控制,禁止直接访问属性,也就是把属性设置为private的, 将getter和setter设置成public的,通过getter和setter访问private的属性。
  4. 通过查看tsc编译ts文件后的代码可以知道:本质上,定义一个类,其实就是声明了一个方法(比如这个类叫Animal,那么编译后的这个方法也叫Animal)。在类中定义的方法(比如这个方法叫fun),其实是定义在了其(也就是Animal的)原型对象上。如果我们在类中定义的属性或方法是静态的,也就是static的,本质上就是将其绑定在了Animal这个方法上(Animal.fun)
  5. 把类当接口使用
  6. let greeterMaker: typeof Greeter = Greeter;如何解释?(备注:Greeter是定义的一个类)声明了一个变量,将Greeter这个类赋值给它,那么这个类是什么类型的呢?它是(typeof Greeter )类型的。其实,我们定义的某个类,它也是类类型(有点拗口,也不知道这么说对不对)的一个实例。就像我们平时 let str: string = ‘hello’; 它的意思是声明了一个变量str,它的类型是string的,‘hello’是string这个类型的一个实例。

泛型,本质就是在使用时,再决定类型,其实也相当于传参,只不过这个参数是个类型

定义

function identity<T>(arg: T): T {
    return arg;
}

使用

let output = identity<string>("myString");  // type of output will be 'string'

如何定义一个泛型类?(泛型接口类似)

class GenericNumber<T> {
    zeroValue: T;
    add: (x: T, y: T) => T;
}

let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };

泛型约束,就是约束泛型只能是哪一类的

interface Lengthwise {
    length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);  // Now we know it has a .length property, so no more error
    return arg;
}

枚举

一组相关的变量应该考虑使用枚举来定义
常量枚举

const enum Enum {
    A = 1,
    B = A * 2
}

常量枚举只能使用常量枚举表达式,并且不同于常规的枚举,它们在编译阶段会被删除。 常量枚举成员在使用的地方会被内联进来。 之所以可以这么做是因为,常量枚举不允许包含计算成员。

类型推断,本质就是TypeScript类型检查器帮我们确定类型

就是在我们没有明确指定类型的情况下,TypeScript类型检查器帮我们推断出类型来,如果我们的调用有问题,就会给出我们提示。

类型兼容性,本质就是类型不一样但结构具有某种相似,就可以互相赋值

这是涉及到了名义类型结构子类型。名义类型就是我们在java中用到的那种类型之间互相赋值的规则。结构子类型就是通过判断结构是否类似以决定是否可以赋值。搞不明白,typescript为何要这样搞?

高级类型

我不是很待见它,后面想起来的时候再来补充吧!

Symbols 原生类型!原生类型!原生类型!就像string等一样

它有点类似于字符串,但每个又是唯一且不相等的,它的作用也就是起了一个符号作用。这个单词本身的意思也是符号。它的用法和字符串有一点类似。

//Symbols是不可改变且唯一的。
let sym2 = Symbol("key");
let sym3 = Symbol("key");
sym2 === sym3; // false, symbols是唯一的

迭代器

当一个对象实现了Symbol.iterator属性时,我们认为它是可迭代的

for..of vs. for..in 语句

for..of 迭代的是值,for..in 迭代的是键

模块

在一个模块里,我们可以有多个导出,就比如

export * from "./StringValidator"; // exports interface StringValidator
export * from "./LettersOnlyValidator"; // exports class LettersOnlyValidator
export * from "./ZipCodeValidator";  // exports class ZipCodeValidator

我们在导入时使用这样的语句

import { ZipCodeValidator } from "./ZipCodeValidator";
let myValidator = new ZipCodeValidator();

有没有想过,其实{ ZipCodeValidator }是一种解构?
以下是一种自动执行的导入:
import "./my-module.js";
我们平时用的ts的导出,应该类似于nodejs中的export.x = ,我们在ts中的export default x应该类似于nodejs中的export =。
但是 export default 语法并不能兼容CommonJS和AMD的exports。
怎么办?
TypeScript提供了export =语法。若使用export =导出一个模块,则必须使用TypeScript的特定语法import module = require("module")来导入此模块。
例子:

//ZipCodeValidator.ts
let numberRegexp = /^[0-9]+$/;
class ZipCodeValidator {
    isAcceptable(s: string) {
        return s.length === 5 && numberRegexp.test(s);
    }
}
export = ZipCodeValidator;
//Test.ts
import zip = require("./ZipCodeValidator");

// Some samples to try
let strings = ["Hello", "98052", "101"];

// Validators to use
let validator = new zip();

// Show whether each string passed each validator
strings.forEach(s => {
  console.log(`"${ s }" - ${ validator.isAcceptable(s) ? "matches" : "does not match" }`);
});

所谓命名空间,其实就是为某些对象(whatever)的集合起了个名字,然后通过命名空间以点的形式访问这些对象。

模块里不要使用命名空间,为什么,嵌套太多了。模块本身已经具有命名空间的作用了。命名空间适用于为那些全局变量包裹一层,然后这些变量就变成命名空间下的一层了。些时,命名空间就起到了为变量归类和避免变量名称冲突的作用。

对于typescript来说,啥叫外部模块?就是不是用typescript语言编写的库,比如用javascript编写的jquery等。typescript是有类型检查的,jquery没有怎么办?我们这些开发人员给它定义类型呗。(后面的命名空间讲到,外部模块改叫模块了?内部模块,也就是用ts写的,改叫命名空间了?)

举个例子,比如有个库叫path,我们可以这样定义它的类型

declare module "path" { // 在命名空间讲到,不建议使用module了,而是使用namespace?
    export function normalize(p: string): string;
    export function join(...paths: any[]): string;
    export let sep: string;
}

这里的module关键字和命名空间作用差不多。其实就是起了个包裹的作用。那怎么用呢?

/// <reference path="path.d.ts"/>
import * as PATH from "path";

这里为啥要用reference呢?是不是declare module "path"的话就不需要用到reference

命名空间

使用命名空间是为了提供逻辑分组和避免命名冲突。模块文件本身就是一个逻辑分组。
模块是要用import或require引入的,命名空间不能这样用。

declare是用来声明全局变量的?
如果没有将命名空间导出,引入它时就要用reference,类似于使用<script>引入?

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

推荐阅读更多精彩内容