TypeScript基础入门

时间:2019-10-06 14:17来源:编程技术
转载地址 本系列文章只为记忆使用,非一步一步学习资料。 项目实践仓库 1.接口概述 https://github.com/durban89/typescript_demo.gittag: 1.0.13 一个简单示例来观察接口是如何工作的: 为了保证后

转载地址

本系列文章只为记忆使用,非一步一步学习资料。

项目实践仓库

1.接口概述

https://github.com/durban89/typescript_demo.gittag: 1.0.13

一个简单示例来观察接口是如何工作的:

为了保证后面的学习演示需要安装下ts-node,这样后面的每个操作都能直接运行看到输出的结果。

function printLabel(labelledObj: { label: string }) {//函数可能会经常如此写,注意格式,一个匿名接口
console.log(labelledObj.label);
}

let myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj);
npm install -D ts-node

类型检查器会查看printLabel的调用.printLabel有一个参数,并要求这个对象参数有一个名为label且类型为string的属性。 需要注意的是,我们传入的对象参数实际上会包含很多属性,但是编译器只会检查那些必需的属性是否存在,并且其类型是否匹配。(属性名称和类型都要匹配才行,有一个不匹配,都是不允许的)
重写上面的例子,这次使用接口来描述:必须包含一个label属性且类型为string:

后面自己在练习的时候可以这样使用

interface LabelledValue {//使用了关键字interface,这个就是把它提出来了,而非使用匿名了
label: string;//必须名称为label,类型为string。切记
}

function printLabel(labelledObj: LabelledValue) {//一个函数,参数为LabelledValue接口类型
console.log(labelledObj.label);
}

let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);//其实传入方法后,此类型被阉割成interface的类型了
LabelledValue接口就好比一个名字,用来描述上面例子里的要求。 它代表了有一个 label属性且类型为string的对象。 需要注意的是,我们在这里并不能像在其它语言里一样,说传给 printLabel的对象实现了这个接口。我们只会去关注值的外形。 只要传入的对象满足上面提到的必要条件,那么它就是被允许的。
npx ts-node src/learn_basic_types.ts

npx ts-node 脚本路径

还有一点值得提的是,类型检查器不会去检查属性的顺序,只要相应的属性存在并且类型也是对的就可以。

TypeScript的核心原则之一是对值所具有的结构进行类型检查。 它有时被称做“鸭式辨型法”或“结构性子类型化”。 在TypeScript里,接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约。

2.可选属性:

继承接口

和类一样,接口也可以相互继承。 这让我们能够从一个接口里复制成员到另一个接口里,可以更灵活地将接口分割到可重用的模块里。如下实例演示

interface Shape { color: string;}interface Square extends Shape { sideLength: number;}let square = <Square> {};square.color = 'red'square.sideLength = 10;

一个接口可以继承多个接口,创建出多个接口的合成接口。如下实例演示

interface Shape { color: string;}interface PenStroke { penWidth: number;}interface Square extends Shape, PenStroke { sideLength: number;}let square = <Square> {};square.color = 'red'square.sideLength = 10;square.penWidth = 10;
interface SquareConfig {
color?: string;//此属性可选,只是加了个?而已
width?: number;//此属性可选
}

function createSquare(config: SquareConfig): {color: string; area: number} {//此方法需要返回一个接口类型为{color: string; area: number}的值,不然会报错
let newSquare = {color: "white", area: 100};//初始化了一个如此的对象

if (config.color) {
newSquare.color = config.color;

}

if (config.width) {//可选的,就需要判断下再使用
newSquare.area = config.width * config.width;

}
return newSquare;
}

let mySquare = createSquare({color: "black"});//这个时候可以如此的,缺少一个
let mySquare = createSquare({});//这个可以如此,都缺少
let mySquare = createSquare({test: "black"});//这个不允许,错误的,字面量不允许传其它属性的
let mySquare = createSquare({color: "white", width: 100});//这个可以的
let mySquare = createSquare({color: "white", width: 100,test:102});//这个不允许,错误的,字面量不允许传其它属性的

混合类型

先前我们提过,接口能够描述JavaScript里丰富的类型。 因为JavaScript其动态灵活的特点,有时你会希望一个对象可以同时具有上面提到的多种类型。一个例子就是,一个对象可以同时做为函数和对象使用,并带有额外的属性。

interface Counter { (start: number): string interval: number; reset(): void;}function getCounter(): Counter { let counter = <Counter>function(start: number) {}; counter.interval = 10; counter.reset = function() {} return counter;}let counter = getCounter()counter;counter.reset();counter.interval = 10.0

在使用JavaScript第三方库的时候,你可能需要像上面那样去完整地定义类型。

但是你如此做:如果使用对象,那么下面几种情况(注意和字面量对象的区别)

接口继承类

当接口继承了一个类类型时,它会继承类的成员但不包括其实现。 就好像接口声明了所有类中存在的成员,但并没有提供具体实现一样。 接口同样会继承到类的private和protected成员。 这意味着当你创建了一个接口继承了一个拥有私有或受保护的成员的类时,这个接口类型只能被这个类或其子类所实现(implement)。当你有一个庞大的继承结构时这很有用,但要指出的是你的代码只在子类拥有特定属性时起作用。 这个子类除了继承至基类外与基类没有任何关系。 例:

class Control { private state: any;}interface SelectableControl extends Control { select(): void;}class Button extends Control implements SelectableControl { select() {}}class TextBox extends Control {}class Image implements SelectableControl { select() {}}

运行后会爆出如下异常

⨯ Unable to compile TypeScript:src/interface_8.ts: error TS2300: Duplicate identifier 'Image'.src/interface_8.ts: error TS2420: Class 'Image' incorrectly implements interface 'SelectableControl'. Property 'state' is missing in type 'Image'.

在上面的例子里,SelectableControl包含了Control的所有成员,包括私有成员state。 因为 state是私有成员,所以只能够是Control的子类们才能实现SelectableControl接口。 因为只有 Control的子类才能够拥有一个声明于Control的私有成员state,这对私有成员的兼容性是必需的。

在Control类内部,是允许通过SelectableControl的实例来访问私有成员state的。 实际上, SelectableControl接口和拥有select方法的Control类是一样的。 Button和TextBox类是SelectableControl的子类(因为它们都继承自Control并有select方法),但Image和Location类并不是这样的。

本实例结束实践项目地址

https://github.com/durban89/typescript_demo.gittag: 1.0.14
class Test//这个没有错误,都没有
{
}

class Test//这个错误的,如果有一个属性,那么必须是color或者width中的一个才行
{
kkk:string;
}

class Test//这个正确,只要包含了一个即可,其它的随便加
{
kkk:string;
width:number;
}
let mySquare = createSquare(new Test());

解决上面几个错误,有两种方法:
(1)使用断言:

class Test
{
kkk:string;
}

let mySquare = createSquare({kkk:"hello"} as SquareConfig);//不会报错了
let mySquare2 = createSquare((new Test()) as SquareConfig);//不会报错了

(2)改变interface的申明方式:

如果 SquareConfig带有上面定义的类型的color和width属性,并且还会带有任意数量的其它属性,那么我们可以这样定义它:

interface SquareConfig {
color?: string;
width?: number;
[propName: string]: any;

}

3.只读属性

一些对象属性只能在对象刚刚创建的时候修改其值。 可以在属性名前用 readonly来指定只读属性:interface Point { readonly x: number; readonly y: number;}可以通过赋值一个对象字面量来构造一个Point。 赋值后, x和y再也不能被改变了。let p1: Point = { x: 10, y: 20 };p1.x = 5; // error!TypeScript具有ReadonlyArray类型,它与Array相似,只是把所有可变方法去掉了,因此可以确保数组创建后再也不能被修改:let a: number[] = [1, 2, 3, 4];let ro: ReadonlyArray= a;

ro[0] = 12; // error!
ro.push(5); // error!
ro.length = 100; // error!
a = ro; // error!

上面代码的最后一行,可以看到就算把整个ReadonlyArray赋值到一个普通数组也是不可以的。 但是你可以用类型断言重写:

a = ro as number[];
readonly vs const

最简单判断该用readonly还是const的方法是看要把它做为变量使用还是做为一个属性。 做为变量使用的话用 const,若做为属性则使用readonly。

4.接口函数类型:这个记住格式即可

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

}

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

}

5.实现接口

与C#澳门金莎娱乐网站,或Java里接口的基本作用一样,TypeScript也能够用它来明确的强制一个类去符合某种契约。

interface ClockInterface {//接口
currentTime: Date;

}
class Clock implements ClockInterface {//继承接口
currentTime: Date;
constructor(h: number, m: number) { }
}

你也可以在接口中描述一个方法,在类里实现它,如同下面的setTime方法一样:

interface ClockInterface {
currentTime: Date;
setTime(d: Date);

}

class Clock implements ClockInterface {
currentTime: Date;
setTime(d: Date) {
this.currentTime = d;
}

constructor(h: number, m: number) { }
}

接口描述了类的公共部分,而不是公共和私有两部分。 它不会帮你检查类是否具有某些私有成员。

6.接口也是可以继承的,并且可以多重继承

interface Shape { color: string;}
interface PenStroke { penWidth: number;}
interface Square extends Shape, PenStroke { sideLength: number;}
let square =<Square>{};//一个空对象,符合Square的接口的约束
square.color = "blue";
square.sideLength = 10;
square.penWidth = 5.0;

7.接口继承类

当接口继承了一个类类型时,它会继承类的成员但不包括其实现。 就好像接口声明了所有类中存在的成员,但并没有提供具体实现一样。 接口同样会继承到类的private和protected成员。 这意味着当你创建了一个接口继承了一个拥有私有或受保护的成员的类时,这个接口类型只能被这个类或其子类所实现(implement)。

当你有一个庞大的继承结构时这很有用,但要指出的是你的代码只在子类拥有特定属性时起作用。 这个子类除了继承至基类外与基类没有任何关系。 例:

class Control {
private state: any;

}

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

class Button extends Control implements SelectableControl {
select() { }
}

class TextBox extends Control {
}

// 错误:“Image”类型缺少“state”属性。

class Image implements SelectableControl {
select() { }

}

class Location {
}

在上面的例子里,SelectableControl包含了Control的所有成员,包括私有成员state。 因为 state是私有成员,所以只能够是Control的子类们才能实现SelectableControl接口。 因为只有 Control的子类才能够拥有一个声明于Control的私有成员state,这对私有成员的兼容性是必需的。

在Control类内部,是允许通过SelectableControl的实例来访问私有成员state的。 实际上, SelectableControl就像Control一样,并拥有一个select方法。 Button和TextBox类是SelectableControl的子类(因为它们都继承自Control并有select方法),但Image和Location类并不是这样的。

编辑:编程技术 本文来源:TypeScript基础入门

关键词: