数据类型
- Enums:列出所有可用值
- Any:any 是默认的类型,其类型的变量允许任何类型的值
- Void:可以用 void 表示没有任何返回值的函数
function alertName(): void {
console.log('My name is muyy')
}
- never:在函数中它通常表现为抛出异常或无法执行到终止点(例如无限循环)
// 返回值为 never 的函数可以是抛出异常的情况
function error(message: string): never {
throw new Error(message);
}
// 返回值为 never 的函数可以是无法被执行到的终止点的情况
function loop(): never {
while (true) {}
}
函数
- 我们可以给每个参数添加类型之后再为函数本身添加返回值类型。
- 在TypeScript 里我们可以在参数名旁使用?实现可选参数的功能。如果带默认值的参数出现在必须参数前面,用户必须明确的传入 undefined 值来获得默认值。
function buildName2(firstName = "鸣", lastName?: string){
console.log(firstName + "" + lastName)
}
let res4 = buildName2("人"); // undefined人
let res5 = buildName2(undefined, "人"); // 鸣人
接口
TypeScript的核心原则之一是对值所具有的结构进行类型检查。在TypeScript里,接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约。
类类型
与 C# 或 Java 里接口的基本作用一样,TypeScript 也能够用它来明确的强制一个类去符合某种契约。
我们可以在接口中描述一个方法,在类里实现它,如同下面的 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) {}
}
继承接口
和类一样,接口也可以相互继承。 这让我们能够从一个接口里复制成员到另一个接口里,可以更灵活地将接口分割到可重用的模块里。
interface Shape{
color: string;
}
interface PenStroke{
penWidth: number;
}
interface Square extends Shape,PenStroke{
sideLength: number;
}
let s = <Square>{};
s.color = "blue";
s.penWidth = 100;
s.sideLength = 10;
函数类型
接口也可以描述函数类型。定义的函数类型接口就像是一个只有参数列表和返回值类型的函数定义。
interface SearchFunc{
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function(source: string,subString: string){
return source.search(subString) !== -1;
};
console.log(mySearch("鸣人","鸣")); // true
console.log(mySearch("鸣人","缨")); // false
泛型
可以使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据。 这样用户就可以以自己的数据类型来使用组件。
export interface TagID<Id, Tag> {
readonly id: Id;
readonly __typename: Tag;
}
export interface TypedItem<T, Id, D, R> extends TagID<Id, T> {
data: D;
relation: R;
}
export enum StudyType {
Study = 'Study'
}
export type Study = TypedItem<
StudyType.Study,
string,
StudyItem,
StudyRelation
>;
export interface StudyRelation {
readonly children_ids: string[];
}
好处1:类型注解/类型检查
JavaScript 是一门弱类型语言,ts可以设定类型,可以使代码更健壮和可维护。
好处2:类型别名
TS 有个非常好用的功能就是类型别名。
类型别名会给一个类型起个新名字。类型别名有时和接口很像,但是可以作用于原始值,联合类型,元组以及其它任何你需要手写的类型。
关键字
使用类型别名可以实现很多复杂的类型,很多复杂的类型别名都需要借助关键字,我们先来了解一下几个常用的关键字:
extends
可以用来继承一个类,也可以用来继承一个interface
keyof
可以用来取得一个对象接口的所有 key 值:
interface Person {
name: string;
age: number;
location?: string;
}
type K1 = keyof Person; // "name" | "age" | "location"
in 可以遍历枚举类型:
type Keys = "a" | "b"
type Obj = {
[p in Keys]: any
} // -> { a: any, b: any }
// 上面 in 遍历 Keys,并为每个值赋予 any 类型。
内置类型别名
- Partial 的作用就是可以将某个类型里的属性全部变为可选项 ?。
- Required 的作用刚好跟 Partial 相反,Partial 是将所有属性改成可选项,Required 则是将所有类型改成必选项,源码如下:
- Exclude 将某个类型中属于另一个的类型移除掉。
- readonly这个类型的作用是将传入的属性变为只读选项。
// node_modules/typescript/lib/lib.es5.d.ts
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
// 给子属性添加 readonly 的标识,如果将上面的 readonly 改成 -readonly, 就是移除子属性的 readonly 标识。
自定义类型别名
- Omit
有时候我们想要继承某个接口,但是又需要在新接口中将某个属性给 overwrite 掉,这时候通过 Pick
和 Exclude
就可以组合出来 Omit
,用来忽略对象某些属性功能:
type Omit<T, K> = Pick<T, Exclude<keyof T, K>>;
// 使用
type Foo = Omit<{name: string, age: number}, 'name'> // -> { age: number }
为什么用ts代替js
ts支持类型注解,是静态类型语言。
用法
constructor
是一种用于创建和初始化class
创建的对象的特殊方法。
在一个构造方法中可以使用super
关键字来调用一个父类的构造方法。ES6 要求,子类的构造函数必须执行一次 super 函数,否则会报错。
enum Color {
red = 'red',
blue = 'blue',
}
const c2 = Color.blue;
const ColorValue = {
red: 'red',
blue: 'blue'
} as const;
type ValueEnum<T> = T[keyof T];
const c = ColorValue.blue;
type Color2 = ValueEnum<typeof ColorValue>;
const allColorValues: Color2[] = Object.values(ColorValue)
泛型:泛型类看上去与泛型接口差不多。 泛型类使用(<>
)括起泛型类型,跟在类名后面。
Object.values():The Object.values()
method returns an array of a given object's own enumerable property values
const object1 = {
a: 'somestring',
b: 42,
c: false
};
console.log(Object.values(object1));
// expected output: Array ["somestring", 42, false]
字符串字面量类型允许你指定字符串必须的固定值。 在实际应用中,字符串字面量类型可以与联合类型,类型保护和类型别名很好的配合。 通过结合使用这些特性,你可以实现类似枚举类型的字符串
type Easing = "ease-in" | "ease-out" | "ease-in-out";
class UIElement {
animate(dx: number, dy: number, easing: Easing) {
if (easing === "ease-in") {
// ...
}
else if (easing === "ease-out") {
}
else if (easing === "ease-in-out") {
}
else {
// error! should not pass null or undefined.
}
}
}
let button = new UIElement();
button.animate(0, 0, "ease-in");
button.animate(0, 0, "uneasy"); // error: "uneasy" is not allowed here
interface Person {
name: string;
age: number;
}
let personProps: keyof Person;
// 'name' | 'age'
为数据创建类。TypeScript 提供的简写形式 —— 用构造函数的参数直接定义属性:
//在hero.ts里使用简写:
export class Hero {
constructor(
public id: number,
public name: string) { }
}
导入了 Hero
类之后,组件的 heroes
属性就可以返回一个类型化的Hero
对象数组了。
import { Component } from '@angular/core';
import { Hero } from './hero';
@Component({
selector: 'app-root',
template: `
<h1>{{title}}</h1>
<h2>My favorite hero is: {{myHero.name}}</h2>
<ul *ngFor="let hero of heroes"><li>
{{hero.name}} </li></ul>
<p *ngIf="heroes.length>3">you have too many hero</p>
`
})
export class AppComponent {
title = "Tour of Heroes";
heroes = [
new Hero(1,"hhh"),
new Hero(2,"hhfh"),
new Hero(3,"hhdsh"),
new Hero(4,"hhsfssh")
];
myHero = this.heroes[0];
}
比较另一种不太有扩展性的方法,不推荐:
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<h1>{{title}}</h1>
<h2>My favorite hero is: {{myHero}}</h2>
<ul *ngFor="let hero of heroes"><li>
{{hero}} </li></ul>
<p *ngIf="heroes.length>3">you have too many hero</p>
`
})
export class AppComponent {
title = "Tour of Heroes";
heroes = ["fbjkfrjk","hdshf","nehe","gegf"]
myHero = this.heroes[0];
}
ts可用''包裹直接换行:
var a = `aaa
bbb
ccc`;
ts可用${}直接在字符串插入变量:
var name = 'forrest';
var getName = function (){
return 'forrest';
}
console.log(`hello ${name}`);
console.log(`hello ${getName()}`);
get() 存取器