假设当前我们有一个Person
类型, 当我们使用Person
类型的时候需要定义它的全部变量,使用起来灵活度太低,以下有几种方法可以让我们更灵活的使用类型:
type Person = {
name: string,
age: number,
hobby: string
}
let obj: Person = { name: 'jack'}
// 类型“{ name: string; }”缺少类型“Person”中的以下属性: age, hobby
Pick通过泛型选择指定的属性生成新的类型
// 类型PickPerson包含类型Person的name和hobby属性
type PickPerson = Pick<Person, 'name' | 'hobby'>
const usePick: PickPerson = { name: 'jack', hobby: 'swim' }
Partial:使用Partial来将已声明的类型中的所有属性标识为可选的。
const usePartial: Partial<Person> = {}
Exclude:用于排除不需要的属性,最后的到的是排除后的属性,常用来配合其他的方法使用。
type PersonKeys = keyof Person; // "name" | "age" | "hobby"
type Age = Exclude<PersonKeys, 'name' | 'hobby'> // 等价于 type Age = "age"
Omit:从声明的类型中删除指定的属性生成新的类型。K extends keyof T
表示K是T的子类型
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
Omit方法中使用了 Exclude 和 Pick方法。结合一个小例子理解这个方法。
type OmitPerson = Omit<Person, 'name'>
// 第一步执行Exclude方法,Exclude, 等价于 'age' | 'hobby'
// 第二步执行Pick方法,Pick 从类型Person中选择 'age' | 'hobby'属性
// 因此type OmitPerson = {age: number, hobby: string }const useOmit1: OmitPerson = { age: 10, hobby: 'swim' }
2. 基础类型
数组定义
let list: number[] = [1, 2, 3];
let list: Array<number> = [1, 2, 3];//数组泛型
枚举:enum类型
enum Color {Red = 1, Green, Blue}; //默认起始值是0,此处设置为从11开始
let colorName: string = Color[2];
alert(colorName);
空值:void类型像是与any类型相反,它表示没有任何类型。 当一个函数没有返回值时,你通常会见到其返回值类型是 void
function warnUser(): void {
alert("This is my warning message");
}
Nevera.
never
类型表示的是那些永不存在的值的类型。 例如, never类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型; 变量也可能是 never类型,当它们被永不为真的类型保护所约束时。b.
never
类型是任何类型的子类型,也可以赋值给任何类型;然而,没有类型是never的子类型或可以赋值给never类型(除了never本身之外)。 即使 any也不可以赋值给never。
// 返回never的函数必须存在无法达到的终点
function error(message: string): never {
throw new Error(message);
}
// 推断的返回值类型为never
function fail() {
return error("Something failed");
}
// 返回never的函数必须存在无法达到的终点
function infiniteLoop(): never {
while (true) {
}
}
类型断言:类型断言好比其它语言里的类型转换,但是不进行特殊的数据检查和解构。 它没有运行时的影响,只是在编译阶段起作用。a. 尖括号”语法:
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
b. as语法:
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
3. 接口
可选属性:带有可选属性的接口与普通的接口定义差不多,只是在可选属性名字定义的后面加一个?符号。只读属性:一些对象属性只能在对象刚刚创建的时候修改其值。 你可以在属性名前用 readonly来指定只读属性
interface Point {
readonly x: number;
readonly y: number;
}
只读属性TypeScript具有ReadonlyArray
类型,它与Array
相似,只是把所有可变方法去掉了,因此可以确保数组创建后再也不能被修改
最简单判断该用readonly
还是const
的方法是看要把它做为变量使用还是做为一个属性。 做为变量使用的话用const
,若做为属性则使用readonly
。
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function(src: string, sub: string): boolean {
let result = src.search(sub);
if (result == -1) {
return false;
}
else {
return true;
}
}
a. 类实现接口
interface ClockInterface {
currentTime: Date;
setTime(d: Date);
}
class Clock implements ClockInterface {
currentTime: Date;
setTime(d: Date) {
this.currentTime = d;
}
constructor(h: number, m: number) { }
}
b. 扩展接口:和类一样,接口也可以相互扩展。 这让我们能够从一个接口里复制成员到另一个接口里,可以更灵活地将接口分割到可重用的模块里,一个接口可以继承多个接口,创建出多个接口的合成接口。
interface Shape {
color: string;
}
interface PenStroke {
penWidth: number;
}
interface Square extends Shape, PenStroke {
sideLength: number;
}
let square = <Square>{};
square.color = "blue";
square.sideLength = 10;
square.penWidth = 5.0;
混合类型:接口能够描述JavaScript里丰富的类型。一个例子就是,一个接口可以同时做为函数和对象使用,并带有额外的属性。
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;
接口继承类:当接口继承了一个类类型时,它会继承类的成员但不包括其实现。 就好像接口声明了所有类中存在的成员,但并没有提供具体实现一样。 接口同样会继承到类的private和protected成员。 这意味着当你创建了一个接口继承了一个拥有私有或受保护的成员的类时,这个接口类型只能被这个类或其子类所实现(implement)
4. 类
构造函数的派生类必须调用super()
,它会执行基类的构造方法。在TypeScript里,成员都默认为 public
,当成员被标记成private
时,它就不能在声明它的类的外部访问。
class Animal {
private name: string;
constructor(theName: string) {
this.name = theName;
}
}
new Animal("Cat").name; // Error: 'name' is private;
protected
修饰符与private
修饰符的行为很相似,但有一点不同,protected
成员在派生类中仍然可以访问。
class Person {
protected name: string;
protected constructor(theName: string) { this.name = theName; }
}
// Employee can extend 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"); // Error: The 'Person' constructor is protected
readonly
修饰符:可以使用readonly
关键字将属性设置为只读的。 只读属性必须在声明时或构造函数里被初始化
class Octopus {
readonly name: string;
readonly numberOfLegs: number = 8;
constructor (theName: string) {
this.name = theName;
}
}
let dad = new Octopus("Man with the 8 strong legs");
dad.name = "Man with the 3-piece suit"; // error! name is readonly.
静态属性:这些属性存在于类本身上面而不是类的实例上,每个实例想要访问这个属性的时候,都要在属性前面加上类名。抽象类:抽象类做为其它派生类的基类使用。i. 它们一般不会直接被实例化。 不同于接口,抽象类可以包含成员的实现细节
ii. abstract关键字是用于定义抽象类和在抽象类内部定义抽象方法。
iii. 抽象类中的抽象方法不包含具体实现并且必须在派生类中实现。类具有 实例部分与静态部分这两个部分
class Greeter {
static standardGreeting = "Hello, there";
greeting: string;
greet() {
if (this.greeting) {
return "Hello, " + this.greeting;
}
else {
return Greeter.standardGreeting;
}
}
}
let greeter1: Greeter; greeter1 = new Greeter();
console.log(greeter1.greet());
let greeterMaker: typeof Greeter = Greeter;
greeterMaker.standardGreeting = "Hey there!";
let greeter2: Greeter = new greeterMaker();
console.log(greeter2.greet());
我们实例化 Greeter
类,并使用这个对象。与我们之前看到的一样。再之后,我们直接使用类。我们创建了一个叫做 greeterMaker
的变量。这个变量保存这个类或者说保存了类构造函数。
然后我们使用 typeof Greeter
,意思是取Greeter
类的类型,而不是实例的类型。或者更确切的说, “我告诉 Greeter
标识符的类型”,也就是构造函数的类型。
这个类型包含了类的所有静态成员和构造函数。之后,就和前面一样,在我们 greeterMaker
上使用new
,创建 Greeter
的实例。
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);
函数重载:方法是为同一个函数提供多个函数类型定义来进行函数重载。 编译器会根据这个列表去处理函数的调用。 下面我们来重载 pickCard
函数。
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. 泛型
类型变量:使返回值的类型与传入参数的类型是相同的。 我们使用了 类型变量,它是一种特殊的变量,只用于表示类型而不是值。
function identity<T>(arg: T): T {
return arg;
}
泛型函数使用:a. 第一种是,传入所有的参数,包含类型参数,明确的指定了T是
string
类型,并做为一个参数传给函数,使用了<>括起来而不是()。
let output = identity<string>("myString"); // type of output will be 'string'
b. 第二种方法更普遍。利用了类型推论 – 即编译器会根据传入的参数自动地帮助我们确定T的类型:
let output = identity("myString"); // type of output will be 'string'
c. 实例:
function loggingIdentity<T>(arg: T[]): T[] {
console.log(arg.length); // Array has a .length, so no more error
return arg; }
function loggingIdentity<T>(arg: Array<T>): Array<T> {
console.log(arg.length); // Array has a .length, so no more error
return arg; }
泛型接口:现在 identities()
所做的是将类型 T 和 U 传递到函数和 Identities
接口中,使我们能够定义与参数类型相关的返回类型
function identities<T, U> (arg1: T, arg2: U):
Identities<T, U> { console.log(arg1 + ": " + typeof (arg1));
console.log(arg2 + ": " + typeof (arg2));
let identities: Identities<T, U> = {
id1: arg1,
id2: arg2
};
return identities; }
泛型类:与接口一样,直接把泛型类型放在类后面,可以帮助我们确认类的所有属性都在使用相同的类型。类有两部分:静态部分和实例部分。 泛型类指的是实例部分的类型,所以类的静态属性不能使用这个泛型类型
7. 类型兼容性
比较两个函数:
let x = (a: number) => 0;
let y = (b: number, s: string) => 0;
y = x; // OK
x = y; // Error
x的每个参数必须能在y里找到对应类型的参数,所以允许赋值。
第二个赋值错误,因为y有个必需的第二个参数,但是x并没有,所以不允许赋值。
let x = () => ({name: 'Alice'});
let y = () => ({name: 'Alice', location: 'Seattle'});
x = y; // OK
y = x; // Error because x() lacks a location property
类型系统强制源函数的返回值类型必须是目标函数返回值类型的子类型。
8. 高级类型 如果一个值是联合类型,我们只能访问此联合类型的所有类型里共有的成员interface Bird {
fly();
layEggs();
}
interface Fish {
swim();
layEggs();
}
function getSmallPet(): Fish | Bird {
// ...
}
let pet = getSmallPet(); pet.layEggs(); // okay
pet.swim(); // errors
用户自定义的类型保护
function isFish(pet: Fish | Bird): pet is Fish {
return (<Fish>pet).swim !== undefined;
}
// 'swim' 和 'fly' 调用都没有问题了
if (isFish(pet)) {
pet.swim();
}
else {
pet.fly(); }
在这个例子里,pet is Fish
就是类型谓词。 谓词是 parameterName is Type
这种形式,parameterName
必须是来自于当前函数签名里的一个参数名。
每当使用一些变量调用isFish
时,TypeScript会将变量缩减为那个具体的类型,只要这个类型与变量的原始类型是兼容的。
class BasicCalculator {
public constructor(protected value: number = 0) { }
public currentValue(): number {
return this.value;
}
public add(operand: number): this {
this.value += operand;
return this;
}
public multiply(operand: number): this {
this.value *= operand;
return this;
} // ... other operations go here ...
}
let v = new BasicCalculator(2)
.multiply(5)
.add(1)
.currentValue();
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)