TypeScript(TS)语法整理
目录
一、简介
TypeScript 是 JavaScript 的超集,包含 ES567等,(有强大的类型系统)
新增了类型系统和完整的面向对象语法
使用 TS 编写的项目更健硕,且更容易扩展和维护,通俗点就是规范
二、TypeScript 环境安装与运行
TS -> JS ,再交给浏览器运行. 类似 Less -> CSS.所以你html里面script标签引用的是index.ts转为js后的index.js文件
打开终端;输入以下命令,就可以看到你的ts跑起来并转为js了:
npm install -g typescript //全局安装
tsc -v //检查版本,是否安装typescript
tsc --init //初始化,创建 tsconfig.json 文件
// tsc 作用: 负责将 ts 代码 转为 浏览器 和 nodejs 识别的 js 代码如:
//直接运行
tsc index.ts // ts转化为js(使用事例)
tsc --watch index.ts // 自动监听ts文件改变并转化为js
-------------------------------------------------
//如果是在node中运行
tsc index.ts
node index.js//跑起来
或者
npm i -g ts-node //一个自动讲ts转为js并运行的node包
ts-node index.ts// 使用事例
三、TypeScript 使用方法
1. 基本数据类型,联合类型
let age: number = 1313
let age1: string = '1313'
let age2: boolean = true
let a:number|string=111// 联合类型(或)可以是number也可以是string
let age3: undefined = undefined
let age4: null = null
//可以把null undefined赋值给其他类型如:
let num2: number = null//报错把tsconfig.json文件里的严格模式关掉就可以了
//例如for循环
for (let index: number = 0; index < 10; index++) {
if (index == 6) {
continue
}
console.log(index)
}
2. 数组,元组
//数组
let names: string[] = ['刚刚', '订单']
let names2: number[] = [1, 2]
let names3: Array<number> = [1, 2]//另一种写法
// 任意类型
var arr: any[] = [1, 2, 3, "a", "b", "c"];
//元组,规定类型,长度的数组
let tup: [string, number] = ['刚刚', 3]
3. 枚举
//枚举
enum Color{
Reds=1,//不赋值默认从0开始,后面递增
Greens,
Blues
};
var colorName: string = Color[2]; //访问第二个枚举子元素Green,key值
console.log(colorName);//Greens
colorName = Color[4];
console.log(colorName);//undefined
var c: Color = Color.Greens;//访问第二个枚举子元素Green,value值
console.log(c);//2
4. 函数(void,剩余参数,可选参数,默认值)
// 函数
function warnUser(): void {
//将函数的返回类型指定为 void,表示该函数不返回任何值
console.log("This is my warning message");
}
function add(x: number, y: number): number {
return x + y;
}
console.log(add(1, 2))
function demo3(info: object): object {
return {
name: '小红'
}
}
demo3({ info: 'kkk' })
-----------------------------
//剩余参数
//...args: string[]表示剩余的参数,放在了一个字符串数组中
function greet(x: number, ...args: string[]): string { // 返回一个字符串
return "Hello World"
}
-------------------------
//将 lastName 设置为可选参数:
function buildName(firstName: string, lastName?: string) {
if (lastName)
return firstName + " " + lastName;
else
return firstName;
}
let result1 = buildName("Bob"); // 正确
// let result2 = buildName("Bob", "Adams", "Sr."); // 错误,参数太多了
let result3 = buildName("Bob", "Adams"); // 正确
---------------------------------
// 设置参数的默认值
function calculate_discoun(price: number, rate: number = 0.50) {
var discount = price * rate;
console.log("计算结果: ", discount);
}
calculate_discoun(1000)
calculate_discoun(1000, 0.30)
------------------------
//正常函数表达式完整写法
const add3: (x: number, y: number) => number = function (x: number, y: number): number {
return x + y
}
5. 对象
//对象
let person: {//对象的类型注解
name: string;
age: number;
sayHi: (name: string) => void; //将函数的返回类型指定为 void,表示该函数不返回任何值
sayHi2: (name: number) => number;
} = {//表示ts中的对象
name: '啦啦啦',
age: 18,
sayHi: function (name: string) {
console.log('lll')
},
sayHi2: function (name: number) {
return name
},
}
6. 接口(是一个能力,一种约束)
解决对象类型注解的复用性
interface Users {
readonly name: string;//只读
// name:string;
// age:number;
age?: number;//?可有可无
}
let p1: Users = {
name: '啦啦啦',
age: 18,
}
function Demo(per: Users) {
return per.name + per.age
}
7. Class
访问修饰符: public/private/protected。
用来修饰描述 类的成员变量 和 成员方法 – 默认是 public(公共的,任何位置都能访问)
private此方法 只允许 在类中访问,不允许在类外面访问,子类也不行。比如说私有方法不能在实例对象中调用
protected此方法 只允许在类中及子类中访问,不允许在类外面访问。比如说私有方法不能在实例对象中调用
class City {
//定义公共字段(属性)
readonly a: string = '33';
b: number;
constructor(name: string, level: number) {
this.a = name;
this.b = level
// 构造函数中可以修改readonly
}
about() {
console.log('我是父调用的方法', this.a)
}
}
class Childer extends City {
constructor(name: string, level: number) {
super(name, level)
}
about() {
console.log('我是子调用的方法')
super.about()
}
}
let cl = new City('哈哈', 5)
cl.about()//我是父调用的方法
8. Class,接口小结
// 小结:接口与接口之间叫继承,类与接口之间叫实现
interface Ifly {
fly()
}
interface ISwim {
swim: () => void
}
interface IMy extends Ifly, ISwim { }//1.一个接口可以继承其他接口
class Per implements Ifly {
//实现接口中的方法
fly() {
console.log('class使用接口')
}
}
//2.一个类可以实现多个接口
class Per2 implements Ifly, ISwim {
//实现接口中的方法
fly() { }
swim() { }
}
9. 类型推论,类型断言
类型推论:
声明变量初始化赋值,函数返回值,可忽略类型注解,ts会自动添加
类型断言:如下
//querySelector获取id时获取到的是element类型,不能访问到src属性等
//有点像强制转换
let img = document.querySelector('#image') as HTMLImageElement
console.dir(img)
类型断言有两种写法:
1.<类型>变量名
2.变量 as 类型
function lei(num6: number | string): number {
if ((<string>num6).length) {
return (num6 as string).length
} else {
return num6.toString().length
10. 泛型
泛型指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定具体类型的一种特性。
function createArray<T>(value: T, count: number): any[] {
const arr: Array<T> = []
for (let index = 0; index < count; index++) {
arr.push(value)
}
return arr
}
const arr33 = createArray<number>(11, 3)
// <T>(字母可以随便命名)里面就是createArray<number>(11, 3)传的number
----------------------------
//可以传入多个泛型参数
function aa2<P, K>(a: P,b:K): [P,K] {
return [a,b]
}
aa2<number,string>(1,'a')
-------------------------------
//泛型接口
interface IBase<T>{
data:Array<T>,//数组的两种写法
name:T[],
}
class UserB implements IBase<number>{
data:[1,2];
name:[1,3]
}
let UserC:IBase<number>={
data:[1,2],
name:[1,3]
}
// function UserC:IBase<string>{
// }
-------------------------------
//泛型约束
interface Ilength{
//接口中有一个length属性
length:number
}
function getLength<T extends Ilength>(x: T): number { // T 为 Ilength的子类型
return x.length//此时不知道x有没有length这个属性,所以用泛型约束
}
console.log(getLength<string>('kkk'))
// console.log(getLength<number>(123))报错,因为number没有length
文章案例完整代码
四、TypeScript 进阶
1. ! 非空断言操作符
// 忽略变量的 undefined | null:
let myFunc = (maybeString: string | undefined | null) => {
const onlyString: string = maybeString; // Error,不能将类型“string | null | undefined”分配给类型“string”。不能将类型“undefined”分配给类型“string”。
const ignoreUndefinedAndNull: string = maybeString!; // Ok
};
2. keyof操作符
// keyof 用于遍历某种类型的属性(可以操作接口、类以及基本数据类型)
// 错误:
function prop1(obj: object, key: string) {
return obj[key];
}
// 我们期望用户输入的属性是对象上已存在的属性,那么如何限制属性名的范围呢?这时我们可以利用 keyof 操作符:
// 正确:
function prop<T extends object, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
/**
* 解释:首先定义了 T 类型并使用 extends 关键字约束该类型必须是 object 类型的子类型,
* 然后使用 keyof 操作符获取 T 类型的所有键,其返回类型是联合类型,
* 最后利用 extends 关键字约束 K 类型必须为 keyof T 联合类型的子类型
*/
3. type
interface 和 type 很相似,类型定义上,很多时候,用两种方式都能实现。
// interface 和 type 很相似,类型定义上,很多时候,用两种方式都能实现。
/**
* 1、对于联合类型、元组类型、基本类型(原始值)我们一般使用类型别名type,interface不支持。
* 2、类型别名type不能extends和implements(class)
* 3、默认导出方式不同
* 4、type 能使用 in 关键字进行类型映射,interface不支持
*/
type Keys = "firstname" | "surname";
type DudeType = {
[key in Keys]?: string;
};
const test6: DudeType = {
firstname: "Pawel",
surname: "Grzybek",
};
// 报错
//interface DudeType {
// [key in keys]: string
//}
4. typeof操作符
// typeof 操作符用于获取变量的类型。因此这个操作符的后面接的始终是一个变量,且需要运用到类型定义当中
type Person = {
name: string;
age: number;
};
let man: Person = {
name: "Semlinker",
age: 30,
};
type Human = typeof man;
// typeof 和 keyof 一起使用---------
const COLORS = {
red: "red2",
blue2: "blue",
};
// 首先通过typeof操作符获取color变量的类型,然后通过keyof操作符获取该类型的所有键,
// 即字符串字面量联合类型 'red' | 'blue'
type Colors = keyof typeof COLORS;
let color: Colors;
color = "red"; // Ok
color = "blue2"; // Ok
// color = "yellow"; // Error ,Type '"yellow"' is not assignable to type '"red" | "blue"'.
5. 函数重载
/**
* 函数重载:
* 重载是方法名字相同,而参数不同,返回类型可以相同也可以不同。
* 每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。
*/
function disp(s1: string): void;
function disp(n1: number, s1: string): void;
function disp(x: any, y?: any): void {
console.log(x);
console.log(y);
}
disp("abc");
disp(1, "xyz");
// let suits = ["hearts", "spades", "clubs", "diamonds"];
// // 定义重载签名
// function greet(person: string): string;
// function greet(persons: string[]): string[];
// // 定义实现签名
// function greet(person: unknown): unknown {
// if (typeof person === 'string') {
// return `Hello, ${person}!`;
// } else if (Array.isArray(person)) {
// return person.map(name => `Hello, ${name}!`);
// }
// throw new Error('Unable to greet');
// }
// console.log(greet(suits[0]));
// console.log(greet(suits));
6. 联合类型及类型守卫
// 联合类型及类型守卫
type NameOrNameArray = string | string[];
function createName(name: NameOrNameArray) {
if (typeof name === "string") {
return name;
} else {
return name.join(" ");
}
}
var greetingMessage = `Greetings, ${createName(["Sam", "Smith"])}`;
// 使用以_开头命名的参数声明不会被未使用参数检查。例如:
function returnNull(_a: any) { // 正确
// function returnNull(a:any) { 错误:'a'声明了,但是没有使用
return null;
}
7. 交叉类型
/**
* 交叉类型,将多个类型合并为一个类型。
* 这让我们可以把现有的多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性。
* 例如, Person & Serializable & Loggable同时是 Person 和 Serializable 和 Loggable。
* 就是说这个类型的对象同时拥有了这三种类型的成员。
*/
function extend<T, U>(first: T, second: U): T & U {
let result = <T & U & object>{};
for (let id in first) {
(<any>result)[id] = (<any>first)[id];
}
for (let id in second) {
if (!result.hasOwnProperty(id)) {
(<any>result)[id] = (<any>second)[id];
}
}
return result;
}
class Person2 {
constructor(public name: string) {}
}
interface Loggable {
log(): void;
}
class ConsoleLogger implements Loggable {
log() {
// ...
}
}
var jim = extend(new Person2("Jim"), new ConsoleLogger());
var n = jim.name;
jim.log();
8. 命名空间
// 命名空间
namespace some {
export interface ISome1 {}
export class Some2 {}
}
/**
* 命名空间,防止命名冲突:
* 在书写全局声明文件时,允许在全局作用域里定义很多类型。
* 我们十分不建义这样做,当一个工程里有许多声明文件时,它会导致无法处理的命名冲突
*/
/// <reference path = "./IShape.ts" />
/// <reference path = "./IShape1.ts" />
/// <reference path = "./IShape2.ts" />
// 三斜线指令仅可放在包含它的文件的最顶端,如果它们出现在一个语句或声明之后,那么它们会被当做普通的单行注释,并且不具有特殊的涵义
function drawAllShapes(shape: Drawing.IShape) {
shape.draw();
}
// drawAllShapes(new Drawing.Circle());
// drawAllShapes(new Drawing.Triangle());
IShape.ts
namespace Drawing {
export interface IShape {
draw():void;
}
}
IShape1.ts
/// <reference path = "IShape.ts" />
namespace Drawing {
export class Circle implements Drawing.IShape {
public draw() {
console.log("Circle is drawn");
}
}
}
IShape2.ts
/// <reference path = "IShape.ts" />
namespace Drawing {
export class Triangle implements Drawing.IShape {
public draw() {
console.log("Triangle is drawn");
}
}
}
9. 声明文件
/**
* 声明文件
* 声明文件以 .d.ts 为后缀
*/
/// <reference path = "Calc.d.ts" />
// 三斜线指令仅可放在包含它的文件的最顶端,如果它们出现在一个语句或声明之后,那么它们会被当做普通的单行注释,并且不具有特殊的涵义
var obj = new Runoob.Calc();
// obj.doSum("Hello"); // 编译错误
console.log(obj.doSum(10));
function isFish(pet: Fish | Bird): pet is Fish {
return (<Fish>pet).swim !== undefined;
}
Calc.d.ts
/**
* .d.ts 类似于一个全局类型文件,类型定义文件
* 在.d.ts文件中声明的变量或者模块,在其他文件中不需要使用import导入,可以直接使用。
* .d.ts文件中我们常常可以看到declare, declare左右就是告诉TS编译器你担保这些变量和模块存在,并声明了相应类型,
* 编译的时候不需要提示错误!
*/
// declare global {
// interface Array<T> {
// toObservable(): Observable<T>;
// }
// }
// 声明模块
declare module Runoob {
export class Calc {
doSum(limit: number): number;
}
}
declare module "koa-respond";
declare module "path" {
export function normalize(p: string): string;
export function join(...paths: any[]): string;
export let sep: string;
}
declare module '*.png'
declare module "myLibrary/*";
// import { readFile } from "myLibrary/fileSystem/readFile`;
// readFile(); // readFile是'any'类型
// 声明方法,常量
declare function myLib(a: string): string;
declare const foo: string[] | null;
// 声明枚举
declare enum Enum {
A = 1,
B,
C = 2,
}
//---------------------------
// 声明命名空间
declare namespace cats {
interface KittySettings {}
}
// interface CatsKittySettings { } //不要