推荐一本开源书籍,TypeScript 入门教程
以下内容为总结这本书籍内容
参数新特性
参数类型
示例代码
// 参数类型
// 6中基本类型 string nubmer any(任意) boolen underfined symol(ES6)
var str: string = "asd";
// 如果给它赋值数字的话将会报错 但是编译成JS不会报错
// str = 123
var a = 1
// 自动类型识别 即使不指定 他会自己识别 也可以调用方法
var str1 = "a" // 也不可以赋值数字
// str1 = 123;
// 如果想让他既可以是数字也可以是字符
var str2: number | string; //这个叫联合类型
// 当调用他的方法的时候,必须是两者共有的方法
str2 = 2;
str2 = "123";
函数和参数指定类型
// 还可以指定为 基本类型中的
// 如果传递的参数不是指定类型会报错 但仍会编译成js
// 可以定义默认值,如果不定义默认值必须穿两个参数
// 定义了之后可以只传一个
// b 是可选参数,记得要处理b没传的时候的情况
function text1(a: number, b?: number, c: number = 1): void {
a++
// console.log(b.length) 如果b没有传 则会报错
// 如果return 会报错
}
//如果传递的参数不是指定类型会报错
// text1('123')
let mySum = function (x: number, y: number): number {
return x + y;
};
// 这个实际上是这样 这里面的第一个=> 不是ES6 中的=> 这个是左边是输入,需要用括号括起来 右边是输出
let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
return x + y;
};
重载(函数的合并)
function reverse(x: number): number;
function reverse(x: string): string;
function reverse(x: number | string): number | string {
if (typeof x === 'number') {
return Number(x.toString().split('').reverse().join(''));
} else if (typeof x === 'string') {
return x.split('').reverse().join('');
}
return 0
}
用接口定义函数的形状
暂时没看懂
自定义类型(接口)
// 自定义类型 接口
interface Person {
readonly id: number; // 只读属性 只允许在第一次给对象赋值的时候赋值,其他不可以。不允许被修改
name: string
age: number
sex?: string // 可选属性
[propName: string]: any; // 任意属性
// 一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集
}
// 定义的变量不允许比接口少或者多了一些属性 (如果定义了可选或者任意属性除外)
// 赋值的时候,变量的形状必须和接口的形状保持一致。
let tom: Person = {
id: 213,
name: 'Tom',
age: 25
};
接口的合并
合并的属性的类型必须是唯一的
interface Alarm {
price: number;
}
interface Alarm {
weight: number;
// price: string;
}
// 相当于
interface Alarm {
price: number;
weight: number;
}
如果 两个price的类型一样,虽然重复 但不会报错
如果类型不一致,则会报错
接口中方法的合并
interface Alarm {
price: number;
alert(s: string): string;
}
interface Alarm {
weight: number;
alert(s: string, n: number): string;
}
// 相当于
interface Alarm {
price: number;
weight: number;
alert(s: string): string;
alert(s: string, n: number): string;
}
数组的类型
注意:
- 数组的项中不允许出现其他的类型
- 用方法插入也不行
「类型 + 方括号」表示法
let fibonacci: number[] = [1, 1, 2, 3, 5];
用接口表示数组(一般不用)
// 表示只要索引是数字 值必须是数组
interface NumberArray {
[index: number]: number;
}
let fibonacci: NumberArray = [1, 1, 2, 3, 5];
类数组
也就是伪数组
any 在数组中的应用
最常见
let list: any[] = ['xcatliu', 25, { website: 'http://xcatliu.com' }];
类型断言
语法
<类型>值
或者
值 as 类型
// 在 tsx 语法(React 的 jsx 语法的 ts 版)中必须用后一种。
例子
// 这样写会报错
function getLength(something: string | number): number {
return something.length;
}
// 这样写也会报错 因为只能访问 公有的方法
function getLength(something: string | number): number {
if (something.length) {
return something.length;
} else {
return something.toString().length;
}
}
// 使用类型断言
function getLength(something: string | number): number {
if ((<string>something).length) {
return (<string>something).length;
} else {
return something.toString().length;
}
}
注意
类型断言不是类型转换,断言成一个联合类型中不存在的类型是不允许的
声明文件和内置对象
需要自行查看
TS进阶
类型别名
类型别名常用于联合类型。
type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {
if (typeof n === 'string') {
return n;
} else {
return n();
}
}
字符串的字面量类型
就是 参数必须是EventNames的子集
type EventNames = 'click' | 'scroll' | 'mousemove';
function handleEvent(ele: Element, event: EventNames) {
// do something
}
handleEvent(document.getElementById('hello'), 'scroll'); // 没问题
handleEvent(document.getElementById('world'), 'dbclick'); // 报错,event 不能为 'dbclick'
类型别名与字符串字面量类型都是使用 type 进行定义。
元组
例子 跟python中的元组差不多 只是定义不一样
// 定义一对值分别为 string 和 number 的元组:
let tom: [string, number] = ['Tom', 25];
// 可以进行单独的访问和赋值等
tom[0] = 'Tom';
tom[1] = 25;
// 但是当直接对元组类型的变量进行初始化或者赋值的时候,需要提供所有元组类型中指定的项。
tom = ['Tom', 25];
// tom = ['Tom']; 报错
类
属性和方法
class Person1 {
// 公有私有属性 也可以定义方法
// protected 类的内部和子类可以访问
// readonly 只读属性
public name2: string
private name1: string
// 构造方法 this 是实例对象 不能被外部访问
constructor(name: string) {
this.name1 = name;
this.name2 = name;
}
eat() {
console.log(`i am eating ${this.name1}`)
}
}
// 实例化对象
let person1 = new Person1('123')
继承
// 类的继承 extends 关键字
class Cat extends Person1 {
constructor(name: string) {
super(name); // 调用父类的 constructor(name)
console.log(this.name2);
}
sayHi() {
return 'Meow, ' + super.eat(); // 调用父类的 eat()
}
}
储存器
// 储存器
class Animal {
constructor(name: string) {
this.name = name;
}
get name() {
return 'Jack';
}
set name(value) {
console.log('setter: ' + value);
}
}
let cat = new Animal('Kitty'); // setter: Kitty
cat.name = 'Tom'; // setter: Tom
console.log(cat.name); // Jack
静态方法
使用 static 修饰符修饰的方法称为静态方法,它们不需要实例化,而是直接通过类来调用
class Animal1 {
static num = 42
static isAnimal(a: any) {
return a instanceof Animal;
}
}
let jack = new Animal('Jack');
Animal1.isAnimal(a); // true
console.log(Animal3.num); // 42
// 报错 不需要实例化 通过类调用
// jack.isAnimal(a); // TypeError: a.isAnimal is not a function
ES7中类用法
ES6 中实例的属性只能通过构造函数中的 this.xxx 来定义
// ES7 中关于类的用法 实例属性
// ES6 中实例的属性只能通过构造函数中的 this.xxx 来定义
class Animal3 {
name = 'Jack';
constructor() {
// ...
}
}
let jack2 = new Animal3();
console.log(jack2.name); // Jack
TypeScript 中类的用法
public private 和 protected
三种 访问修饰符
public修饰公有的,任何地方都可以访问。所有属性和方法默认publicprivate私有的,不能在声明它的类的外部访问protected修饰受保护的,它和private类似,区别是它在子类中也是允许被访问的
readonly
只读属性关键字,只允许出现在属性声明或索引签名中。
示例
class Animal {
readonly name;
public constructor(name) {
this.name = name;
}
}
let a = new Animal('Jack');
console.log(a.name); // Jack
a.name = 'Tom'; // 报错
注意readonly 和其他访问修饰符同时存在的话,需要写在其后面。
示例
class Animal {
// public readonly name;
public constructor(public readonly name) {
this.name = name;
}
}
抽象类
abstract 用于定义抽象类和其中的抽象方法
抽象类是不允许被实例化的
抽象类中的抽象方法必须被子类实现
abstract class Animal {
public name;
public constructor(name) {
this.name = name;
}
public abstract sayHi();
}
class Cat extends Animal {
public sayHi() {
console.log(`Meow, My name is ${this.name}`);
}
}
let cat = new Cat('Tom');
如果实例化 Animal对象和没有实现 sayHi 方法都会报错
类的类型
与接口类似
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
sayHi(): string {
return `My name is ${this.name}`;
}
}
let a: Animal = new Animal('Jack');
console.log(a.sayHi()); // My name is Jack
泛型
定义:泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
- 泛型可以有多个类型参数
示例代码
// 代码缺陷 没有准确的定义返回值的类型
// Array<any> 允许数组的每一项都为任意类型 Array代表返回一个数组 类型any
function createArray(length: number, value: any): Array<any> {
let result = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
console.log(createArray(3, 'x')) // ['x', 'x', 'x']
// 经过用泛型改良
function createArray1<T>(length: number, value: T): Array<T> {
let result: T[] = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
// 调用的时候指定他的类型 也可以不指定让它自己识别
createArray1<string>(3, 'x'); // ['x', 'x', 'x']
// 泛型可以有多个类型参数
function swap<T, U>(tuple: [T, U]): [U, T] {
return [tuple[1], tuple[0]];
}
console.log(swap([7, 'seven'])); // ['seven', 7]
// 泛型约束
// 普通
function loggingIdentity<T>(arg: T): T {
// console.log(arg.length); // 由于事先不知道它是哪种类型,所以不能随意的操作它的属性或方法
return arg;
}
泛型约束
// 泛型约束
interface Lengthwise {
length: number;
}
function loggingIdentity1<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
loggingIdentity1('asd')
// 调用 loggingIdentity 的时候,传入的值不包含length属性,那么报错
// loggingIdentity1(7)
// 多类型参数互相约束 没看懂
// T 继承 U,这样就保证了 U 上不会出现 T 中不存在的字段。
function copyFields<T extends U, U>(target: T, source: U): T {
for (let id in source) {
target[id] = (<T>source)[id];
}
return target;
}
let x = { a: 1, b: 2, c: 3, d: 4 };
console.log(copyFields(x, { b: 10, d: 20 }))
泛型接口 泛型类 等
类实现接口
不同类之间有一些公有的特性
例子
// 定义接口
interface Alarm {
alert(): void;
}
interface Light {
lightOn(): void;
lightOff(): void;
}
// 接口的继承
interface LightableAlarm extends Alarm {
lightOn(): void;
lightOff(): void;
}
// 定义类
class Door {
}
// 定义一个类继承Door 同时实现接口 Alarm
class SecurityDoor extends Door implements Alarm {
alert() {
console.log('SecurityDoor alert');
}
}
// 类可以实现多个接口 也可以把 Alarm, Light替换为 LightableAlarm
class Car implements Alarm, Light {
alert() {
console.log('Car alert');
}
// 如果少些接口类中的任意一个都会报错
lightOn() {
console.log('Car light on');
}
lightOff() {
console.log('Car light off');
}
}
混合类型
以下内容
个人感觉
再讲
ES6
字符串新特性
多行字符串
双``,作用 :拼接字符串
示例代码
// 多行字符串 用``来表示 可以换行
var str: string = `a
asd
asd
asd
aaa`;
编译完成后的代码
"use strict";
var str = "a\nasd\nasd\nasd\naaa";
字符串模板
作用:在字符串中插入变量或者函数
示例代码:
// 字符串模板
var Myname: string = 'wuyichen'
var age: number = 12
var getName = function () {
return 'wuyichen'
}
// 注意 只能在``里面用,如果是单引号或双引号的则按字符串执行
console.log(`hello ${Myname}`)
console.log(`hello ${getName()}`)
console.log("hello ${Myname}")
编译完成后的代码
// 字符串模板
var Myname = 'wuyichen';
var age = 12;
var getName = function () {
return 'wuyichen';
};
// 注意 只能在``里面用,如果是单引号或双引号的则按字符串执行
console.log("hello " + Myname);
console.log("hello " + getName());
console.log("hello ${Myname}");
自动拆分字符串
第一个参数是字符串模板的值
第二个是 第一个变量
第二个是第三个变量
function text(tem: any, name: string, age: number) {
console.log(tem)
console.log(name)
console.log(age)
}
text`my name is ${Myname},my age is ${age}`
// 编译成js输出结果
/*输出结果
[ 'my name is ', ',my age is ', '' ]
wuyichen
12
*/
函数新特性
spread和rest 运算符
声明任意数量的方法
// spread和rest 运算符
function f1(...args: any) {
args.forEach(function (arg: any) {
console.log(arg)
});
// 这样里面不可以限制值 不报错
// args.forEach(element => {
// console.log(element)
// });
}
f1(1, 2, 3, 4, 5, 6)
f1(1, 2, 3, 4, 5)
// 还可以这样 不能跟此文件夹下的ts变量重名
let ab: number[] = [9, 8, 7, 6, 5] // 数组类型的定义
f1(...ab)
generator函数
控制函数的执行过程,可以手工暂停和回复
目前没办法学。打印不出来
destructuring 析构表达式
通过表达式拆分数组或者对象 成任意数量的变量
跟ES6中的解构一样
对象
function getIBM() {
return {
code: 'IBM',
price: 123,
price2: {
price3: 456,
price4: 789
},
a: 'aaa',
b: 'bbb'
}
}
// 起别名 里面有其他指也不影响这个的使用
let { code: condex, price, price2, price2: { price3 } } = getIBM()
// 如果返回的是一个对象的话,则price2是一个对象,里面的可以可以通过price2: { price3 }拿到里面的属性
console.log(condex)
console.log(price)
数组
参考ES6的解构
循环和表达式
表达式
箭头表达式 ES6一样 有一个参数不需要()
主要用来解决this 指针的问题
var sum = (a: number, b: number) => a + b
循环
forEach
// forEach 不能被break
let aa: any[] = [4, 5, 6, 7, 8, 9]
// ts中不能添加
// aa.desc = "456789"
aa.forEach(value => console.log(value))
for in
for (let a in aa) {
console.log(a)
// 如果想打印值 需要这样打印
console.log(aa[a])
}