最近准备重构项目想使用ts,所以就了解下ts最基本的一些知识,ts官网地址是:www.tslang.cn/docs/handbo… 下边所有代码均来自ts官网,
1.基础类型
大约15种,分别是:布尔,数字,数组,元组,枚举,any, viod ,null, undefined, never,Object, 类型断言,关键字let
// 1布尔
let isDone: boolean = false
// 2数字
let isNumber: number = 6
// 3字符
let isName: string = 'ncy'
/**
* 数组
* 第一种表示方法
* */
let list1: number[] = [1, 2, 3, 4]
// 第二种表示方法 Array<元素类型>
let list2: Array<number> = [1, 2, 3, 4]
// 元组 表示一个已知元素数量和类型的数组,各元素的类型不必相同
let x: [number, string] = [1, '2']
// 枚举
enum Color { Red = 1, Green, Blue }
let c: Color = Color.Green
// Any
let notSure: any = 4
notSure = "1111"
notSure = false
let list: any[] = [1, true, "free"];
list[1] = 100;
// Void 它表示没有任何类型 当一个函数没有返回值时,你通常会见到其返回值类型是 void
function warnUser(): void {
console.log("这是个测试")
}
// 声明一个void类型的变量没有什么大用,因为你只能为它赋予undefined和null:
let unusable: void = undefined;
// Null 和 Undefined TypeScript里,undefined和null两者各自有自己的类型分别叫做undefined和null
// Never 表示的是那些永不存在的值的类型。
function error(message: string): never {
throw new Error(message)
}
function fail() {
return error('Something failed')
}
// Object 表示非原始类型
declare function create(o: object | null): void;
create({ prop: 0 });
/**
* 类型断言
* 其一是“尖括号”语法:
* */
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
// as语法
let someValue1: any = '1'
let strLength1: number = (someValue as string).length
2.变量声明
主要是var let const 以及解构,前三个经常用,说下解构
// var let const 解构
let input = [1, 2]
let [first, second] = input
let [first1, ...rest] = [1, 2, 3]
// 默认值
function a(b:{c:string,d?:number}) {
let {c, d = 1 } = b
}
3.接口
接口的属性分为可选属性和只读属性,可选属性有友好的提示,而且在定义数据类型的时候也更加灵活,建议使用。只读属性比较时候定义一些配置。
函数类型,可索引类型,类类型、继承接口、混合类型、接口继承类等6种
继承接口类似于函数的继承
/**
* 可选属性
*
* 可选属性的好处之一是可以对可能存在的属性进行预定义,好处之二是可以捕获引用了不存在的属性时的错误。
* 如果属性拼错,就会得到一个错误提示
* */
interface SquareConfig {
color?:string;
width?:number;
[propName: string]: any
}
function createSquare(config: SquareConfig): {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 squareOptions = {colour: 'red', width: 100}
// let mySquare = createSquare({color: 'black', opacity: 0.5, width: 100} as SquareConfig)
let mySquare = createSquare(squareOptions)
/**
* 只读属性
*
* */
interface Point {
readonly x: number;
readonly y: number;
}
let p1: Point = {x: 10, y: 20}
p1.x = 5 // 无法分配到 "x" ,因为它是只读属性。ts(2540)
let a: number[] = [1, 2, 3, 4]
let ro: ReadonlyArray<number> = a
ro[0] = 12
ro.push(5)
ro.length = 100
a = ro
// 可以用类型断言重写:
a = ro as number[]
// 函数类型
interface SearchFunc {
(source: string,subString: string): boolean
}
let mySearch: SearchFunc;
mySearch = function(src: string,sub: string) {
let result = src.search(sub)
return result > -1
}
/**
* 可索引的类型
* */
interface StringArray {
[index: number]: string
}
let myArray: StringArray
myArray = ["Bob", "Fred", ""]
let myStr:string = myArray[0]
class Animal {
name: string
}
class Dog extends Animal {
breed: string
}
interface NotOkay {
[x: string]: Dog,
}
// 类类型
interface ClockInterface {
currentTime: Date;
setTime(d: Date);
}
class Clock implements ClockInterface {
currentTime: Date;
setTime(d: Date) {
this.currentTime = d;
}
constructor(h:number, m:number) {}
}
// 类静态部分与实例部分的区别
interface ClockConstructor {
new (hour: number, minute: number): ClockInterface
}
interface ClockInterface {
tick()
}
function createClock(ctor: ClockConstructor, hour: number, minute: number):ClockInterface {
return new ctor(hour, minute)
}
class DigitalClock implements ClockInterface {
constructor(h:number, m: number){}
tick(){
console.log('beep beep')
}
}
class AnalogClock implements ClockInterface {
constructor(h:number, m: number){}
tick(){
console.log('beep beep')
}
}
let digital = createClock(DigitalClock, 12, 17);
let analog = createClock(AnalogClock, 7, 32);
// 继承接口
interface Shape {
color: string;
}
interface PenStroke {
penWidth: number;
}
interface Square extends Shape, PenStroke {
sideLength: number;
}
let square = <Square> {}
square.color = 'black'
square.sideLength = 100
square.penWidth = 50
// 混合类型
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;
}
class Button extends Control implements SelectableControl {
select() {}
}
class TextBox extends Control {
select() {}
}
class Image implements SelectableControl {
select() {}
}
class Location {}
4.类
主要内容有继承,公共,私有与受保护的修饰符,readonly修饰符,存取器,静态属性,抽象类。
**继承主要是超类(基类),子类(派生类)的继承。如果超类中有函数,那么子类需要有super(),它会执行基类的构造函数。 **
// 继承
class Animal {
move(distanceInMeters: number = 0) {
console.log(`Animal moved ${distanceInMeters}m.`);
}
}
class Dog extends Animal {
bark() {
console.log('Woof! Woof!');
}
}
const dog = new Dog();
dog.bark();
dog.move(10);
dog.bark();
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: number = 5) {
console.log('Slithering...')
super.move(distanceInMeters)
}
}
class Horse extends Animal {
constructor(name: string) {super(name)}
move(distanceInMeters = 45) {
console.log('Galloping');
super.move(distanceInMeters)
}
}
let sam = new Snake('Sammy the Python')
let tom: Animal = new Horse('Tommy the Palomino')
sam.move();
tom.move(34);
公共,私有与受保护的修饰符
属性一般都默认为public,如果设置为private,那么只能在自己声明的类中访问。同时在比较的时候,,并不在乎它们从何处而来,如果所有成员的类型都是兼容的,我们就认为它们的类型是兼容的。
protected修饰符与 private修饰符的行为很相似,但有一点不同, protected成员在派生类中仍然可以访问
class Animal {
private name: string;
constructor(theName: string) { this.name = theName; }
}
class Rhino extends Animal {
constructor() {super('Rhino')}
}
class Employee {
private name: string
constructor(theName: string) {this.name = theName}
}
let animal = new Animal('Goat')
let rhino = new Rhino()
let employee = new Employee('Bob')
animal = rhino
animal = employee // 错误: Animal 与 Employee 不兼容.
class Person {
protected name: string;
constructor(name: string) {this.name = name}
}
class Employee extends Person {
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}.`
}
}
let howard = new Employee('howard', "Sales")
console.log(howard.getElevatorPitch())
console.log(howard.name) // 属性“name”受保护,只能在类“Person”及其子类中访问。ts(2445)
class Person {
protected name: string;
protected constructor(theName: string) { this.name = theName; }
}
// Employee 能够继承 Person
class Employee extends Person {
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}.`;
}
}
let howard = new Employee("Howard", "Sales");
let john = new Person("John"); // 错误: 'Person' 的构造函数是被保护的.
readonly修饰符,只可读
class Octopus {
readonly name: string;
readonly numberOfLegs: number = 8;
constructor (theName: string) {
console.log(theName)
this.name = theName;
}
}
let dad = new Octopus("Man with the 8 strong legs");
dad.name = "Man with the 3-piece suit"; // 错误! name 是只读的.
存取器
class Employee {
fullName: string;
}
let employee = new Employee();
employee.fullName = "Bob Smith";
if(employee.fullName) {
console.log(employee.fullName)
}
let passcode = 'secret passcode'
class Employee {
private _fullName: string;
get fullName(): string {
return this._fullName
}
set fullName(newName: string) {
if (passcode && passcode === 'secret passcode') {
this._fullName = newName;
} else {
console.log('Error: Unauthorized update of employee!')
}
}
}
let employee = new Employee()
employee.fullName = 'Bob Smith'
if (employee.fullName) {
alert(employee.fullName)
}
静态属性,注意下静态属性的获取Grid.origin.x
class Grid {
static origin = {x: 0, y: 0};
calculateDistanceFromOrigin(point: {x: number; y: number;}) {
let xDist = (point.x - Grid.origin.x);
let yDist = (point.y - Grid.origin.y);
return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale;
}
constructor (public scale: number) { }
}
let grid1 = new Grid(1.0); // 1x scale
let grid2 = new Grid(5.0); // 5x scale
console.log(grid1.calculateDistanceFromOrigin({x: 10, y: 10}));
console.log(grid2.calculateDistanceFromOrigin({x: 10, y: 10}));
抽象类
抽象类做为其它派生类的基类使用。 它们一般不会直接被实例化。 不同于接口,抽象类可以包含成员的实现细节。 abstract关键字是用于定义抽象类和在抽象类内部定义抽象方法。
abstract class Animal {
abstract makeSound(): void;
move(): void {
console.log('roaming the earch...');
}
}
5.函数
主要是函数的参数定义,剩余参数和this与箭头函数
完整的函数类型,在函数和返回值类型之前使用=>符号,如果没有返回值,需要指定void
let myAdd: (baseValue: number, increment: number) => number = function(x: number, y: number): number { return x + y; };
推断类型,在一边指定类型另一边没有指定类型的情况下,ts自动识别出类型来
// The parameters `x` and `y` have the type number
let myAdd: (baseValue: number, increment: number) => number =
function(x, y) { return x + y; };
可选参数和默认参数,可选参数可以让我们在传值时,非必要值不传,可选参数需要在必选参数后边,如果没有指定类型,那么就和之前的类型一致。默认参数和可选类型有相似的效果。
function buildName(firstName: string, lastName?: string) {
if (lastName)
return firstName + " " + lastName;
else
return firstName;
}
let result1 = buildName("Bob"); // works correctly now
let result2 = buildName("Bob", "Adams", "Sr."); // error, too many parameters
let result3 = buildName("Bob", "Adams"); // ah, just right
默认参数
function buildName(firstName: string, lastName = "Smith") {
return firstName + " " + lastName;
}
let result1 = buildName("Bob"); // works correctly now, returns "Bob Smith"
let result2 = buildName("Bob", undefined); // still works, also returns "Bob Smith"
let result3 = buildName("Bob", "Adams", "Sr."); // error, too many parameters
let result4 = buildName("Bob", "Adams"); // ah, just right
剩余参数 剩余参数,就是可以在接参的过程中只接部分参数,剩余参数在另外一个数组里
function buildName(firstName: string, ...restOfName: string[]) {
return firstName + " " + restOfName.join(" ");
}
let employeeName = buildName("Joseph", "Samuel", "Lucas", "MacKinzie");
// 或者
function buildName(firstName: string, ...restOfName: string[]) {
return firstName + " " + restOfName.join(" ");
}
let buildNameFun: (fname: string, ...rest: string[]) => string = buildName;
this,一般需要我们给一个显示的参数
function f(this: void) {
// make sure `this` is unusable in this standalone function
}
函数的重载
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 (typeof x == "object") {
let pickedCard = Math.floor(Math.random() * x.length);
return pickedCard;
}
// Otherwise just let them pick the card
else if (typeof x == "number") {
let pickedSuit = Math.floor(x / 13);
return { suit: suits[pickedSuit], card: x % 13 };
}
}
let myDeck = [{ suit: "diamonds", card: 2 }, { suit: "spades", card: 10 }, { suit: "hearts", card: 4 }];
let pickedCard1 = myDeck[pickCard(myDeck)];
alert("card: " + pickedCard1.card + " of " + pickedCard1.suit);
let pickedCard2 = pickCard(15);
alert("card: " + pickedCard2.card + " of " + pickedCard2.suit);
6.泛型
泛型的概念:它的作用就是可复用性的提升,一个组件支持多种类型。实质的价值在我看来就是省事!!!
1.基础
function identity<T>(arg: T): T {
return arg;
}
有两种使用方式
第一种:传入所有的参数,包含类型参数:
let output = identity<string>("myString")
第二种: 利用了类型推论 -- 即编译器会根据传入的参数自动地帮助我们确定T的类型
let output = identity("myString");
2.使用泛型变量
function loggingIdentity<T>(arg:T): T {
console.log(arg.length) // 类型“T”上不存在属性“length”。
return arg
}
function loggingIdentity<T>(arg:T[]): T[] {
console.log(arg.length) // 类型“T”上不存在属性“length”。
return arg
}
对比以上代码,区别在于加了[],它是个元素类型是T的数组,并返回类型是T的数组,我们把泛型变量T当做类型的一部分使用,增加了灵活性。
例如下:
function loggingIdentity<T>(arg: Array<T>): Array<T> {
console.log(arg.length); // Array has a .length, so no more error
return arg;
}
3.泛型类型
我的理解是这个就是给函数做了规范,比如你的传值和返回值等
function identity<T>(arg:T):T { return arg }
let myIdentity: <T>(arg:T) => T = identity
例如上边写的,函数的类型为T,参数类型是T,返回为T等,赋值给myIdentity,它的类型为T,可以给它传的参数为T,返回的参数也是T。并且这个泛型的参数名改为什么都行,A,B,C,D都成,只需要数量和使用方式上一致。如下
function identity<T>(arg:T): T { return arg }
let myIdentity:<U>(arg:U) => U = identity
还可以使用字面量定义泛型函数
function identity<T>(arg:T): T {return arg}
let myIdentity:{<U>(arg:U): U } = identity
字面量的方式可以让我们来定义泛型的接口,如下
interface GemericIdentityFn {
<T>(arg:T): T
}
function identity<T>(arg:T): T {return arg}
let myIdentity:GemericIdentityFn = identity
感觉这种有点厉害了,直接定义了整个函数的接口。还有这种
interface GemericIdentityFn<T> {
(arg:T): T
}
function identity<T>(arg:T): T {return arg}
let myIdentity:GemericIdentityFn<number> = identity
整体区别不大,但是第二种的好处可以指定泛型类型。
4.泛型类
class GenericNumber<T> {
zeroValue: T;
add:(x:T, y:T) => T
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = (x, y) => {return x + y}
和泛型接口相似度还挺高,我们定义了类的泛型,在new 对象的时候,可以执行类的泛型,当前为number,那么我们修改指定类型改为string。如下
class GenericNumber<T> {
zeroValue: T;
add:(x:T, y:T) => T
}
let stringNumeric = new GenericNumber<string>();
stringNumeric.zeroValue = ''
stringNumeric.add = (x, y) => {return x + y}
console.log(stringNumeric.add(stringNumeric.zeroValue, "test"))
5.泛型的约束
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg:T): T {
console.log(arg.length)
return arg
}
T继承Lengthwise,所有我们的参数必须包含length且类型为number
后边还有两个例子,我不太懂,有懂的可以指点下,谢谢!!!
在泛型约束中使用类型参数
function getProperty(obj: T, key: K) {
return obj[key];
}
let x = { a: 1, b: 2, c: 3, d: 4 };
getProperty(x, "a")
getProperty(x, "m")
在泛型里使用类类型
在TypeScript使用泛型创建工厂函数时,需要引用构造函数的类类型
function create<T>(c: {new(): T; }): T {
return new c();
}
使用原型属性推断并约束构造函数与类实例的关系。
class BeeKeeper {
hasMask: boolean;
}
class ZooKeeper {
nametag: string;
}
class Animal {
numLegs: number;
}
class Bee extends Animal {
keeper: BeeKeeper;
}
class Lion extends Animal {
keeper: ZooKeeper;
}
function createInstance<A extends Animal>(c: new () => A): A {
return new c();
}
createInstance(Lion).keeper.nametag; // typechecks!
createInstance(Bee).keeper.hasMask; // typechecks!
7.枚举
使用枚举我们可以定义一些带名字的常量。 使用枚举可以清晰地表达意图或创建一组有区别的用例。 TypeScript支持数字的和基于字符串的枚举。
1.数字枚举
enum Direction {
Up = 1,
Down,
Left,
Right
}
数字枚举的值有自增的行为,但每个值是不同的,当初始定义第一个值后,其余的自增。
2.字符串枚举
没有自增行为,但是可读的值
enum Direction {
Up = "UP",
Down = "DOWN",
Left = "LEFT",
Right = "RIGHT",
}
3.计算的和常量成员
枚举成员如果是枚举的第一个成员且没有初始化器,会被赋予值0,如果它是数字常量,成员还是依次递增。
enum E1 { X, Y, Z }
enum E2 {
A = 1, B, C
}
enum FileAccess {
// constant members
None,
Read = 1 << 1,
Write = 1 << 2,
ReadWrite = Read | Write,
// computed member
G = "123".length
}
4.联合枚举与枚举成员的类型
非计算的常量枚举成员的子集:字面量枚举成员
字面量枚举成员是指不带有初始值的常量枚举成员,或者是值被初始化为
- 任何字符串字面量(例如: "foo", "bar", "baz")
- 任何数字字面量(例如: 1, 100)
- 应用了一元 -符号的数字字面量(例如: -1, -100)
而且可以通过联合枚举捕获到一些比较错误
enum ShapeKind {
Circle,
Square,
}
interface Circle {
kind: ShapeKind.Circle;
radius: number;
}
interface Square {
kind: ShapeKind.Square;
sideLength: number;
}
let c: Circle = {
kind: ShapeKind.Square,
// ~~~~~~~~~~~~~~~~ Error!
radius: 100,
}
enum E {
Foo,
Bar,
}
function f(x: E) {
if (x !== E.Foo || x !== E.Bar) {
// ~~~~~~~~~~~
// Error! Operator '!==' cannot be applied to types 'E.Foo' and 'E.Bar'.
}
}
5.运行时的枚举
会默认number
enum E {
X, Y, Z
}
function f(obj: { X: number }) {
return obj.X;
}
// Works, since 'E' has a property named 'X' which is a number.
f(E);