Typescript的基本使用
Typescript的安装,通过npm全局安装Typescript
npm install typescript -g
编译Typescript的文件,typescript的文件以ts文件格式结尾,需要借助编译器编译成js文件后才可以运行,编译指令
tsc xxxx.ts
通过上面的方式编译,比较繁琐,因此都会在项目根目录中新建一个 tsconfig.json
的配置文件,用于设置ts文件的编译方式等
{
/*
*
* 包含的路径
* ** 所有目录,包括子目录, * 所有文件
*/
"include": ["./src/**/*"],
/*
*
* 不需要被编译的文件目录
*
*/
"exclude": [],
/*
*
* 编译选项
*
*/
"compilerOptions": {
/* 编译后的目标兼容版本 */
"target": "ES6",
/* 模块化规范 */
"module": "ES6",
/* 打包后目录 */
"outDir": "./dist",
/* 是否编译JS文件 */
"allowJs": false,
/* 是否检查JS代码语法规范 */
"checkJs": false,
/* 是否移除注释 */
"removeComments": false,
/* 不生产编译后的文件 */
"noEmit": false,
/* 当有错误时,不生产编译后的文件 */
"noEmitOnError": true,
/* 设置编译后的文件为严格模式 */
"alwaysStrict": true,
/* 不允许隐式的any类型 */
"noImplicitAny": true,
/* 不允许不明确类型的this */
"noImplicitThis": true,
/* 严格检查空值 */
"strictNullChecks": true,
/* 开启全部严格检查 */
"strict": true
// ...
}
}
Typescript的基本语法
定义变量的基本类型
// 定义数字类型
const num: number = 1;
// 定义字符串类型
const str: string = 'joe';
// 定义布尔值类型
const bool: boolean = true
注意:null
和 undefined
有且只有1个值,因此声明变量为 null
或 undefined
后就不能再进行赋值操作
let _test1: null;
let _test2: undefined;
_test1 = 1; // ×
_test2 = 1; // ×
如果声明了一个变量,但是没有为这个值赋值,那么这个值默认为 undefined
let num: number; // num的值为undefined
如果既没有声明类型,也没有赋值,那么这个值默认为 any
类型
let num; // num被推导为any类型
null
和 undefined
在某些情况下,会有隐藏的问题。例如
let num: num = 1;
num = null;
num.toFixed(2); // 这种情况下,编辑器不会报错,但是实际运行是有问题的
为了避免出现上面的情形,就需要对 null
和 undefined
开启严格检查模式,开启方式是在 tsconfig.json
文件内加入一行
{
"compileOptions": {
...
// 设置严格检查
"strictNullChecks": true
}
}
基本类型类型的包装类型
在上面定义基本变量的时候,:
后面跟的类型都为小写,小写表示这是一个基本类型的数据,如果大写,那么它表示为基本类型的包装类型,例如
let str: string = 'joe' // 定义一个基本类型的字符串
str = new String("1") // × new 出来的字符串,就是个包装类型,不能赋值给简单类型
定义变量为对象类型
对象内的属性比较少的时候,直接写成行内式
const obj: {x: number; y: string} = {
x: 1,
y: 'joe'
}
对象内的属性如果多的时候,写成 接口
式
// 使用 interface 关键字定义接口
interface MyObject {
x: number;
y: string;
// 任意类型
[prop: string]: any;
}
// 对象使用接口
const obj: MyObject {
x: 1,
y: 'joe'
}
定义变量为数组类型
typescript中数组的存储类型必须一致,所以在标注数组类型时,同时要标注数组中存储的数据类型
let arr: Array<number> = [1, 2, 3]; // 简写形式:let arr: number[] = ...
arr.push('11'); // 错误
arr.push(22); // 正确
定义变量为元组类型
元组类似数组,但是初始化的个数及对应位置,需要和标注的一致,并且越界数据,必须是标注中的类型之一
let arr: [string, number] = ['hello', 2];
arr.push('1'); // 正确
arr.push(1); // 正确
arr.push(true); // 越界数据类型错误
定义枚举类型
enum HTTP_CODE {
SUCCESS = 200,
ERROR = 201,
URL
}
HTTP_CODE.URL; // 202
枚举类型的注意点:
- key不能是数字
- value可以是数字,称为数字类型枚举,如果没有值默认是0
枚举值可以省略,如果省略,则:
- 第一个枚举值默认0
- 非第一个枚举值为上一个数字枚举值 + 1
- 枚举值为只读(常量),定义后不可以被修改
定义任意类型
有时候并不确定这个值到底是什么类型,就可以标注为 any
类型
const a: any = 1;
any
类型的注意点:
- 一个变量申明了未赋值且未标注情况下,默认为
any
- 任何类型值都可以赋值给any类型
- any类型有任意属性和方法
- 标注为any类型,意味着放弃对该值的类型检查,同时也放弃了编辑器的提示
定义unknow类型
unknow类型属于安全版的any类型。例如,由于any可以赋值给任意类型,下面的demo,会报错,并且无法检查到
let a: any = '1';
let b: number = 1;
b.toFixed(2);
b = a;
// 无法检测出
b.toFixed(2);
换成 unknow
类型
let a: unknown = '1';
let b: number = 1;
b.toFixed(2);
b = a; // 报错。不能将unknow类型赋值给其他类型
b.toFixed(2);
使用 unknow
的注意点:
- unknow类型只能赋值给unknow、any
- unknow类型没有任何属性和方法
定义函数无返回值类型
表示没有任何数据的类型,通常用于标注无返回值函数的返回值类型,函数默认标注类型为 void
function fn():void {
console.log("joe")
}
定义函数为never类型
当一个函数永远不可能执行 return
的时候,返回的就是 never
,与 void
不同,void是执行了 return ,只是没有值,never是不会执行 return,比如抛出错误,导致函数终止执行
function fn(): never {
throw new Error('error');
}
定义函数类型的返回值
函数类型可以标注返回值类型、参数的类型,例如
function fn(x: number): number {
return x;
}
接口的使用
接口的基本使用
interface Point {
x: number;
y: number;
}
const point: Point = {
x: 1,
y: 1
}
接口可选属性
接口可以定义可选的属性,使用 ?
进行标注, ?
表示该值是可选的
interface Point {
x: number;
y: number;
z?: string;
}
接口属性只读
接口可以定义只读的属性,使用 readonly
进行标注,标注后,表示该属性值不可被修改
inteface Point {
readonly x: number;
readonly y: number;
}
const point: Point = {
x: 1,
y: 2
}
接口任意类型
interface Point {
x: number;
y: string;
[key: string]: any;
}
const point = {
x: 1,
y: 'hello',
z: 1
}
通过接口描述函数
// 通过接口定义
interface Func {
(e: Event): void;
}
const fn: Func = (e: Event) => {};
// 直接定义
function fn(el: HTMLElement, callback: (e: Event) => number) {}
const div = document.querySelector('div');
fn(div, function () {
return 1;
});
接口重名合并
如果写了2个重名的接口,会将这2个接口进行合并
interface Point {
x: number;
y: number;
}
interface Point {
z: string;
}
const point: Point = {
x: 1,
y: 1,
z: '11'
};
联合类型
如果一个变量可以使用多个类型,可以使用 |
进行分割表示或
function fn(el: HTMLElement, attr: string, value: string | number) {}
const div = document.querySelector('div');
fn(div, 'a', 1);
交叉类型
interface O1 {
x: number;
}
interface O2 {
y: number;
}
const o: O1 & O2 = Object.assign({}, { x: 1 }, { y: 2 });
注意:上面会报错,说Object上面没有assign方法,此时只需要在tsconfig.json中,添加es的lib库即可解决
{
"compilerOptions": {
...
"lib": ["ES6"]
}
}
字面量类型
在实际使用中,经常会有这种情况,只允许接受几个固定的值,其他的值不允许接受时,就可以使用字面量类型
function fn(direction: 'left' | 'right') {}
fn('left');
类型别名
继续上方的字面量类型,如果字面量类型的值过多,此时就应当使用类型别名
// 普通使用
type direction = 'left' | 'right';
function fn(direction: direction) {}
fn('left');
// 定义函数
type fn = (a: number) => number
类型推导
在实际开发中,每次定义变量,参数等都要写这些类型,很是麻烦,于是就有了类型推导,会自动根据上下文代码进行推导出类型
const a = '111' // 此时,a的类型会自动认定为字符串类型
类型断言
默认的类型推导范围可能比较广,如果需要缩小范围,就需要使用类型断言
const img = <HTMLImageElement>document.querySelector('img');
if (img) {
img.src = '';
}
函数标注
函数标注有以下几种方式
/* 第一种 */
function fn(a: number): number {
return 1;
}
fn(1);
/* 第二种 */
const fn2: (a: number) => number = function (a) {
return 1;
};
fn2(2);
/* 第三种 */
type CallBack = (num: number) => number;
const fn3: CallBack = function (num) {
return 1;
};
fn3(1);
/* 第四种 */
interface Fn {
(num: number): number;
}
let fn4: Fn = function (num) {
return 1;
};
fn4(2);
可选参数
function fn(el: HTMLElement, attr: string, val?: number) {}
fn(document.querySelector('div'), 'width', 1);
参数默认值
function fn(attr: 'left' | 'right' = 'left') {}
fn('left');
剩余参数
interface Rest {
[key: string]: any;
}
function fn(target: Array<number>, ...rest: Array<Rest>) {}
fn([1]);
函数中this标注方式
interface T {
x: number;
fn:(x: number) => void
}
const obj: T = {
x: 1,
fn(this: T, x: number){
this.x
}
}
函数重载
例如有这样一个例子,以下函数中,第一个参数为dom元素,第二个参数为display或opacity,第三个参数为数字或字符串
function setStyle(el: HTMLElement, attr: 'display' | 'opacity', val: 'block' | 'none' | number) {
...
}
上面的参数这样写没问题,但是想要实现传入display时,第三个值必须是block或none,于是就可以利用函数重载实现
function fn(el: HTMLElement, attr: 'display', val: 'block' | 'none'): void
function fn(el: HTMLElement, attr: 'opacity', val: number): void
function fn(el: HTMLElement, attr: string, val: any){}
类
类的基本使用
class Person {
/* 定义在实例上的属性 */
name = '杜恒';
/* 定义在类上的属性 */
static age = 18;
/* 定义在实例上的方法 */
static say() {
console.log(1);
}
}
const duHeng = new Person()
console.log(duHeng)
console.log(Person)
类的构造器
class Dog {
name: string;
age: number;
/* 实例化对象时,会调用这个构造器函数 */
constructor(name: string, age: number) {
/* this指向当前实例化出的实例 */
console.log(this);
this.name = name;
this.age = age;
}
}
const WangCai = new Dog('旺财', 1);
const XiaoBai = new Dog('小白', 2);
console.log(WangCai);
console.log(XiaoBai);
在typescript中,定义类的静态属性,使用public关键字,使用后,会自动设置成参数,并且会赋值,上面的写法等同于下面的
class Dog {
constructor(public name: string, public age: number) {
// ...
}
}
类里面的属性修饰符
class User {
constructor(
// 公开属性,默认public
public host: number
// 可以访问,但是不能修改
readonly id: number,
// 只能在内部和子类访问,但是不能被外部修改
protected username: string,
// 外部、子类都不可以访问,只有当前能够访问
private password: string
) {
}
}
const user = new User(1, 'Joe', '123')
user.id; // 可以访问
user.id = 1; // × 不可以被修改
user.username; // × 不可读
user.password; // × 不可读
上面的password设置成私有属性后,外部不可以被访问修改,但是如果就想让外部读取和修改,就需要使用到 寄存器
class User {
constructor(private _password: string) {}
// 用于设置私有属性密码
set password(newPassword: string) {
this._password = newPassword;
}
// 用于获取私有属性密码
get password(): string {
return '****';
}
}
const user = new User('1');
user.password = '111';
user.password
类当中的静态成员
type FileList = 'gif' | 'png' | 'jpg'
class User {
// static 定义的属性只存在于类中,不存在于实例中
static readonly FILE_LIST: Array<FileList> = ['gif', 'png']
}
抽象类
/*
*
* 声明成抽象的类,无法new这个类
* 抽象类就是专门用来被继承的类
*
*/
abstract class Component {
constructor(public props: any) {}
/* 如果声明了方法为抽象的,那么必须声明这个类也是抽象的 */
abstract render(): void;
}
class SonComponent extends Component {
render() {}
}
new SonComponent(1);
规范类,实现类继承多个接口
interface Info {
getInfo(): string;
}
class A implements Info {
getInfo() {
return '';
}
}
泛型
很多时候,标注的具体类型不能确定,因此就需要使用到泛型
interface Inter {
length: number;
}
function fn<T extends Inter>(obj: T) {
return obj.length;
}
fn('sdasd');
const obj1 = {
x: 1,
y: 2
}
const obj2 = {
a: 1,
b: 2
}
function fn<T>(obj: T, k: keyof T) {
return obj[k]
}
fn<typeof obj1>(obj1, 'x')
interface Res<T> {
code: number;
message?: string;
data: T;
}
interface data {
name: string;
age: number;
}
async function getData<U>(url: string) {
let response = await fetch(url);
let data: Promise<Res<U>> = await response.json();
return data;
}
(async function () {
const res = await getData<data>('/user');
})();
评论 (2)