您的位置:首页 > 其它

浅谈 TypeScript 特性

2017-06-28 22:05 232 查看
浅谈 TypeScript 特性

*readonly

用来约束一个对象的属性, 在对象刚创建时能赋值, 往后赋值均会报错, 在类中使用 readonly 只能在构造函数中赋初始值。

interface Point {
readonlyx:number;
readonlyy:number;
}
const p:Point = {x:10,y:
20 };
p.x =
5; // error [ts] Cannot assign to 'x' because it is a constant or a read-only property.!

*ReadonlyArray<T>

创建一个不可变的数组, 谈 ReadonlyArray<T>
的目的只是说明一下 typescript 会内置了一些实用的接口供开发者使用。

let a:number[] = [1,2,3,4];
let ro:ReadonlyArray<number> =a;
ro.push(2);// error [ts] Property 'push' does not exist on type 'ReadonlyArray<number>'.
a.push(2);

a = ro;
// error [ts] Type 'ReadonlyArray<number>' is not assignable to type 'number[]'

a = roasstring[];// error [ts] Type 'ReadonlyArray<number>'
cannot be converted to type 'string
a = roasany[];// ok 类型断言重写 但是如下依旧会报错
a = roasnumber[];// ok 类型断言重写 但是如下依旧会报错

ro.push(2);// error [ts] Property 'push' does not exist on type 'ReadonlyArray<number
a.push(2);

*类型断言

通过上面的 as 和 下面的几个 as 类型断言, 大概可判断类型断言 x as y, x 类型约束比 y 要更细致, 便能成功(纯属个人理解, 官方具体定义很飘渺不是很懂)

// ok
interface SquareConfig {
color?:string;
width?:number;
}
function createSquare(config:SquareConfig) {
// ...
}
let mySquare =createSquare({width:100,opacity:0.5
}as SquareConfig);

// error [ts] Type '{ width: number; opacity: number; }' cannot be converted to type 'SquareConfig'.
interface SquareConfig {
color:string;
width:number;
}
function createSquare(config:SquareConfig) {
// ...
}
let mySquare =createSquare({width:100,opacity:0.5
}as SquareConfig);// ok

interface SquareConfig {
color:string;
width:number;
}
function createSquare(config:SquareConfig) {
// ...
}
let mySquare =createSquare({width:100,opacity:0.5,color:"my
name is string" }as
SquareConfig);

// 更好的方法 这样定义接口
SquareConfig 添加其他参数不会报错了
interface SquareConfig {
color?:string;
width?:number;
[propName:string]:any;
}

*类类型接口

当你操作类和接口的时候,你要知道类是具有两个类型的:静态部分的类型和实例的类型。

如下例子, 类一般实现的约束实例部分的接口, 当需要使用 new 关键字实例化的时候才会对其静态部分进行检查

interface ClockConstructor {
new (hour:number,minute:number):ClockInterface;
//ok
// new
(hour:number,minute:number)ok
}
interface ClockInterface {
tick();
}

function createClock(ctor:ClockConstructor,hour:number,minute:number):ClockInterface
{
returnnewctor(hour,minute);
}

class DigitalClockimplementsClockInterface {
constructor(h:number,m:number) { }
tick() {
console.log("beep beep");
}
}

let digital =createClock(DigitalClock,12,17);

*构造函数工厂制造类

js版本构造函数工厂制造类

function counter(){
}
counter.interval =123;
counter.reset =function () { };

在 ts 中需要如下约束

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

*private 与 protected

如下的例子可以看出, private修饰 不能在实例中和子类中访问; protected能在子类中访问。

class Person {
private name:string;
constructor(name:string) {this.name =name;
}
}

class EmployeeextendsPerson {
private department:string; //private

constructor(name:string,department:string) {
super(name)
this.department =department;
}

public getElevatorPitch() {
return `Hello, my name is${this.name}
and I work in${this.department}.`;
// [ts] Property 'name' is private and only accessible within class 'Person'.
}
}

let howard =newEmployee("Howard","Sales");
console.log(howard.getElevatorPitch());
console.log(howard.name);// error

class Person {
protected name:string; //protected
constructor(name:string) {this.name =name;
}
}

class EmployeeextendsPerson {
private department:string;

constructor(name:string,department:string) {
super(name)
this.department =department;
}

public getElevatorPitch() {
return `Hello, my name is${this.name}
and I work in${this.department}.`;
// ok
}
}

let howard =newEmployee("Howard","Sales");
console.log(howard.getElevatorPitch());
console.log(howard.name);// error

* 不一样的set get

需要提醒的别和 java 中的 getFullName, setFullName 语法弄混!在 ts 中 中间上有一个空格的存在。

class Employee {
private _fullName:string;
get fullName():string {
return this._fullName;
}
set fullName(newName:string) {
// 如果满足一定条件才能修改值成功
}
}
let employee =newEmployee();
employee.fullName ="Bob Smith";

*类的实例部分与静态部分

在类类型接口中其实我们就谈过这部分内容,
声明一个类其实也就声明类的静态和实例部分。

知识点:

typeof
Greeter 取的是 Greeter类静态部分的类型, 

那么如下例子中
greeterMaker类 只能访问其静态成员。 

static standardGreeting ="Hello, there";
constructor() {
}
greeting: string;
greet() {
if (this.greeting) {
return "Hello, " +this.greeting;
}
else {
return Greeter.standardGreeting;
}
}
}
interface StaticClass {// 在 类类型声明中 就谈过的在 类实例化时 对类静态成员就行检查
new ();
standardGreeting:
string;
}
function createGreeter (ctr:StaticClass):Greeter {
return newctr();
}
let greeter1:Greeter;
greeter1 = createGreeter(Greeter);
console.log(greeter1.greet());
let greeterMaker:typeofGreeter =Greeter;
// 从这可以知道 typeof 取的是类的静态部分的类型 typeof Greeter 相当于语句 type a = typeof Greeter; let greeterMaker: a = Greeter;
greeterMaker.standardGreeting ="Hey there!";
let greeter2:Greeter =newgreeterMaker();
console.log(greeter2.greet());
// 既然 typeof 取的是类的静态部分的类型 那么 interface StaticClass 也是对静态部分进行约束检查的, 果不其然下面的语句是成立相等的 !
let clasA:StaticClass;
let clasB:typeofGreeter;
clasB = clasA;
clasA = clasB;


* this

学习使用JavaScript里
this
就好比一场成年礼,
下面简要说一下typescript里面的this。

可以看出在嵌套return function 里面 this 指向不再是deck 对象, 而是windows, 故报错!

var deck = {
suits: ["hearts","spades",
"clubs","diamonds"],
cards: Array(52),
createCardPicker:
function () {
return function () {
var pickedCard =Math.floor(Math.random() *52);
var pickedSuit =Math.floor(pickedCard /13);
return { suit:this.suits[pickedSuit],card:
pickedCard %13 };
};
}
};
var cardPicker =deck.createCardPicker();
var pickedCard =cardPicker();
alert("card: " +pickedCard.card +" of " +
pickedCard.suit);
// Uncaught TypeError: Cannot read property '0' of undefined

在上面的情况下, 我们便需要利用typescript的特性, 以定义接口的形式, 对变量, 参数...加以约束, this 也需要显示声明其类型, 才不会在类型问题上出错 。

interface Card {
suit: string;
card: number;
}
interface Deck {
suits: string[];
cards: number[];
createCardPicker(this:Deck): ()
=>Card;
}
let deck:Deck = {
suits: ["hearts","spades",
"clubs","diamonds"],
cards: Array(52),
// NOTE: The function now explicitly specifies that its callee must be of type Deck
createCardPicker:
function(this:
Deck) {
return () => {
let pickedCard =Math.floor(Math.random() *52);
let pickedSuit =Math.floor(pickedCard /13);

return {suit:this.suits[pickedSuit],card:
pickedCard %13};
}
}
}
let cardPicker =deck.createCardPicker();
let pickedCard =cardPicker();
alert("card: " +pickedCard.card +" of " +
pickedCard.suit);

* 箭头函数和 this

如下例子会报错, 因为传入的 h.onClickBad 仅仅被当作一个函数, 此时里面的 this 已经丢失了

class Handler {
info: string;
onClickBad (e:string) {
this.info =e;
}
}
let h =new
Handler();
function ss(cb) {
cb("sss")
}
ss(h.onClickBad);
console.log(h.info)

当然如果这样定义 ss 函数是没问题的, 此时 this 不会丢失, 但是这样更改 ss 函数是不是相当于改了需求?这是不合理的方式没有从源头解决问题。

function ss(){
h.onClickBad("sss")
}

此时来看看箭头优势, 改写一下 Handler onClickBad为箭头函数方式。

class Handler {
info: string;
onClickBad = (e:string) =>{
this.info =e;
}
}

为什么改成这样不会丢失 this , 我们可以看看箭头函数干了什么事, 当编译成 js 文件时

var Handler = (function () {
function Handler() {
var _this =this;
this.onClickBad =function (e) {
_this.info =e;
};
}
return Handler;
}());
var h =new
Handler();
function ss(cb) {
cb("sss");
}
ss(h.onClickBad);
console.log(h.info);

是的, 正如我们平时书写 js 文件时, 为了防止 this 丢失我们通常也是用变量存储, 箭头函数其实就在帮我们干这事。

* 重载

typescript 中的重载无力吐槽, 没有使用的冲动。if else 嵌套去选择性调用, 极度不友好, java等强类型重载机制就清晰明了多了。

let suits = ["hearts","spades",
"clubs","diamonds"];
function pickCard(x: {suit:string;
card:number; }[]):
number;
function pickCard(x:number): {suit:string;
card:number; };
function pickCard(x):any {
// Check to see if we're working with an object/array
// if so, they gave us the deck and we'll pick the card
if (typeofx ==
"object") {
let pickedCard =Math.floor(Math.random() *x.length);
return pickedCard;
}
// Otherwise just let them pick the card
else if (typeofx ==
"number") {
let pickedSuit =Math.floor(x /13);
return { suit:suits[pickedSuit],card:
x % 13 };
}
}

* 泛型 与 类 类型

下面的例子也证明了之前说的几个知识点

1. 声明一个类, 实际上也声明了其 实例部分和静态部分, 所以类也能作为接口使用(因为有实例部分属性的约束检查和接口的属性检查原理是一致的)

2. 对一个等于函数的变量约束有两种方式

a. 

const a: (name:string)
=> string = (name:
string)=> {
return name
}

b.

const a: {(name:string):
string} = (name:string)=> {
return name
}

3. c: new (name: string)=> A , 对参数c的约束在类 类型中也谈过, 实际上是对静态部分进行约束检查, new
(name: string)=> A 也可以写出 {
(name: string): A }  。

class Animal {
numLegs: number;
}
class Lionextends
Animal {
constructor(publicname:
string){
super()
}
keeper: "hello word!";
}
function createInstance<Aextends
Animal>(c:new (name:
string)=>A):A {
return newc(name);
}
createInstance<Lion>(Lion).keeper;

* 枚举

使用枚举我们可以定义一些有名字的数字常量。

1. 如下, 为普通枚举

enum FileAccess {
// constant members
None,
Read = 1 <<
1,
Write = 1 <<
2,
ReadWrite = Read |
Write,
// computed member
G = "123".length
}

将会编译成一个很有意思的对象, 具有反向映射的特点, 看看代码就知道这个对象的特点了

var FileAccess;
(function (FileAccess) {
// constant members
FileAccess[FileAccess["None"] =
0] = "None";
FileAccess[FileAccess["Read"] =
2] = "Read";
FileAccess[FileAccess["Write"] =
4] = "Write";
FileAccess[FileAccess["ReadWrite"] =
6] = "ReadWrite";
// computed member
FileAccess[FileAccess["G"] =
"123".length] =
"G";
})(FileAccess || (FileAccess = {}));

相当于 声明一个如下的对象

var FileAccess ={
"None":
0,
0:
"None"
}

2. 如下为常数枚举

如下例子用const 修饰, 不能有需要计算的成员, 如 G = "123".leng 等是需要计算出结果的

让我们看看编译的结果

const enum
FileAccess2 {
None,
Read = 1 <<
1,
Write = 1 <<
2,
ReadWrite = Read |
Write,
// G = "123".length [ts] In 'const' enum declarations member initializer must be constant expression.
}

const a =
FileAccess2.ReadWrite

让我们看看编译的结果,是的 常数枚举在编译时会被删除

var
a = 6
/* ReadWrite */;
3. 外部枚举

使用
declare 修饰, declare 一般用在声明文件中 如放在global.d.ts中进行全局声明

declare enum
FileAccess3 {

None,

Read = 1 <<
1,

Write = 1 <<
2,

ReadWrite = Read |
Write,

// G = "123".length [ts] In 'const' enum declarations member initializer must be constant expression.

}

const a2 =
FileAccess3.ReadWrite
编译成

var a2 =
FileAccess3.ReadWrite;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息