typeScript笔记(一)

177 阅读12分钟

typeScript笔记(一)

对于刚入门 TypeScript 的小伙伴 使用线上的 TypeScript Playground 来学习新的语法或新特性。

TypeScript Playground:www.typescriptlang.org/play/

1.基础类型

const a:string = 'aa'
const b:number = 123
const c: boolean = true
const d:null = null
const e:undefined = undefined

默认情况下nullundefined是所有类型的子类型,可以把nullundefined赋值给其它任何类型:

let num: number = 1;
num = null;
num = undefined;

// null 和 undefined 赋值给 boolean
let bool: boolean = false;
bool = null;
bool = undefined;

// null 和 undefined 赋值给 object
let obj: object = {};
obj = null;
obj = undefined;

2.数组

let arr :string[] = ['a','b','c']  //元素全部是字符串数组
let arr1: number[] = [1,2,3]  //元素全部是数字的数组
let arr2: boolean[] = [true,false,true] //元素全部是布尔值的数组
let arr3: Object[] = [{'name':'a'}]  //元素是对象的数组
let list: Array<number> = [1, 2, 3]; // Array<number>泛型语法

3.对象

对于对象类型的描述我们需要用关键词interface来进行类型约束:

let obj={
  name:'小明',
  age:12,
  sex:'男',
  isFun:true
}
interface UserInfo{
  name:string,
  age:number,
  sex:string,
  isFun:boolean
}

let obj:UserInfo={
  name:'小明',
  age:12,
  sex:'男',
  isFun:true
}

而接口加上数组类型,就可以描述一个成员是对象的数组类型:

let userList:UserInfo[] = [
  {
    name:'小明',
    age:12,
    sex:'男',
    isFun:true,
    info:{
      class:'3年2班',
      score:80
    }
  },
  {
    name:'小红',
    age:11,
    sex:'女',
    isFun:true,
    info:{
      class:'3年2班',
      score:90
    }
  },
  {
    name:'小波',
    age:12,
    sex:'男',
    isFun:true,
    info:{
      class:'3年2班',
      score:70
    }
  }
]

假如我需要将sex类型变为可选类型可以在接口中定义可选标记 加个问号

interface User{
  name:string,
  age:number,
  sex?:string
}

4.枚举enum,

使用枚举我们可以定义一些带名字的常量。 使用枚举可以清晰地表达意图或创建一组有区别的用例。 TypeScript 支持数字的和基于字符串的枚举。

enum Direction {
  NORTH,
  SOUTH,
  EAST,
  WEST,
}
let dir: Direction = Direction.NORTH;

对于es6中的map和set数据类型进行类型约束

let set = new Set<number>()
set.add(1);
set.add('2'); // X 类型“string”的参数不能赋给类型“number”的参数。
const map = new Map<number, string>();
//约束key只能是number类型 value只能是srting类型
map.set(1, '1');
map.set('2', '2'); // X 类型“string”的参数不能赋给类型“number”的参数。

5.Any类型

在 TypeScript 中,任何类型都可以被归为 any 类型。这让 any 类型成为了类型系统的顶级类型(也被称作全局超级类型)

any 类型本质上是类型系统的一个逃逸舱。作为开发者,这给了我们很大的自由:TypeScript 允许我们对 any 类型的值执行任何操作,而无需事先执行任何形式的检查。比如:

let notSure: any = 666;

notSure = "Semlinker";

notSure = false;

let value: any;

value.foo.bar; // OK
value.trim(); // OK
value(); // OK
new value(); // OK
value[0][1]; // OK

6. Unknown 类型

就像所有类型都可以赋值给 any,所有类型也都可以赋值给 unknown。这使得 unknown 成为 TypeScript 类型系统的另一种顶级类型(另一种是 any)。下面我们来看一下 unknown 类型的使用示例:

let value: unknown;

value = true; // OK
value = 42; // OK
value = "Hello World"; // OK
value = []; // OK
value = {}; // OK
value = Math.random; // OK
value = null; // OK
value = undefined; // OK
value = new TypeError(); // OK
value = Symbol("type"); // OK

unknown 类型只能被赋值给 any 类型和 unknown 类型本身,被赋值其他类型会报错

let value: unknown;

let value1: unknown = value; // OK
let value2: any = value; // OK
let value3: boolean = value; // Error
let value4: number = value; // Error
let value5: string = value; // Error
let value6: object = value; // Error
let value7: any[] = value; // Error
let value8: Function = value; // Error

7.Tuple 元组类型

如果存储的元素数据类型不同,则需要使用元组。

元组中允许存储不同类型的元素,元组可以作为参数传递给函数

let mytuple = [10,"Runoob"];

元组的特点:

  1. 元组可以包含多种不同类型的数据,但每个数据的类型是固定的,按照声明的顺序排列。

  2. 元组的长度是固定的,一旦声明,就不能改变。你不能往元组中添加或删除元素。

  3. 可以通过索引来访问元组中的元素,索引从 0 开始。

  4. 元组的类型根据声明时的类型推断来确定,可以在声明时明确指定类型。

  5. 如果访问超出了元组的索引范围,TypeScript 将会报错。

8.Function(函数)

在TS中函数也是一种数据类型,可以使用Function来表示一个泛用的函数。

如果你希望函数返回的是一个空:

const normal = ():void=>{

}

有返回值的正常函数

const fn: Function = function (a: number, b: number) {
  return a + b
}

我们可以在函数括号后面增加返回值类型

const fn: Function = function (a: number, b: number): number {
  return a + b
}

箭头函数的写法

const fn: Function = (a: number, b: number): number => {
  return a + b
}

9.泛型

泛型就是给我们的代码增加一种相对宽泛的类型约束。在TypeScript中,我们定义一个变量,我们可以赋予其一种确定的类型。使得我们的代码具有更好的维护性,但是在增强代码的可维护性同时,我们又要考虑代码应该具有一定的灵活性。使得在未来,代码也能被复用。于是泛型就在这个背景下出现了

image-20231113145244105转存失败,建议直接上传图片文件

其中 T 代表 Type,在定义泛型时通常用作第一个类型变量名称。但实际上 T 可以用任何有效名称代替。除了 T 之外,以下是常见泛型变量代表的意思:

  • K(Key):表示对象中的键类型;
  • V(Value):表示对象中的值类型;
  • E(Element):表示元素类型。
const  printFun = <T>(value: T): T => {
  console.log(value);
  return value;
};

printFun(233);

其实并不是只能定义一个类型变量,我们可以引入希望定义的任何数量的类型变量。比如我们引入一个新的类型变量 U,用于扩展我们定义的 identity 函数:

function identity <T, U>(value: T, message: U) : T {
  console.log(message);
  return value;
}

console.log(identity<number, string>(68, "Semlinker"))

image-20231113150406293

9.1泛型接口

interface 接口名称 {
  属性名: 属性类型
  函数名(参数类型列表): 返回值类型
}
interface GenericIdentityFn<T> {
  (arg: T): T;
}

interface是我们在使用TS的时候,最常用的关键字。

interface Person {
  name: string;
  age: number;
}
const me: Person = {
  name: "sunny",
  age: 18,
};
//改造一下
interface Person<J, K> {
  name: J;
  age: K;
}
const me: Person<string, number> = {
  name: "sunny",
  age: 18,
};

10.extends

extends 是 typeScript 中的关键字。在 typeScript 的类型编程世界里面,它所扮演的角色实在是太重要了,所以,我们不得不需要重视它,深入学习它。在我看来,掌握它就是进入高级 typeScript 类型编程世界的敲门砖。但是,现实是,它在不同的上下文中,具体不同的,相差很大的语义。如果没有深入地对此进行梳理,它会给开发者带来很大的困惑。

extends的几个语义

  • 用于表达类型组合;
  • 用于表达面向对象中「类」的继承
  • 用于表达泛型的类型约束;
  • 在条件类型(conditional type)中,充当类型表达式,用于求值。

10.1 extends 与 类型组合/类继承

extends 可以跟 interface 结合起来使用,用于表达类型组合。

interface Animal {
  name: string
}

interface Dog extends Animal {
  sayHello: () => void
}
// dog具有Animal的name属性。
const dog: Dog = {
  name: 'tom',
  sayHello: function() {

  }
}

10.2约束类的继承

class Animal {
  name: string;

  constructor(name: string) {
    this.name = name;
  }
}

class Dog extends Animal {
  breed: string;

  constructor(name: string, breed: string) {
    super(name);
    this.breed = breed;
  }
}

const myDog = new Dog("Fido", "Golden Retriever");
console.log(myDog.name); // 访问父类属性
console.log(myDog.breed); // 访问子类属性

10.3约束泛型类型参数

interface Person<J, K> {
  name: J;
  age: K;
}
const printFun = <T extends Person<string, number>, S>(
    person: T,
    msg: S
): S => {
  console.log(person.age);
  console.log(person.name);
  return msg;
};
printFun({ name: "sunny", age: 18 }, "success");

11.接口类型

11.1 基本用法

interface FullName{
  firstName:string
  lastName:string
}

let obj = {
  firstName:'Jonathan',
  lastName:'Lee'
};

//{firstName, lastName}使用了解构赋值
function say({firstName, lastName}:FullName):void {
  console.log(`我的姓名是:${firstName}_${lastName}`);
}
say(obj);

11.2 接口的写法

interface 接口名称 {
  属性名: 属性类型
  函数名(参数类型列表): 返回值类型
}
interface Animal {
  color: string
  showColor: () => string
}

11.3 只读属性

interface Animal {
  readonly color: string
}
const animal: Animal = {
  color: "black"
}
animal.color = "white"

上述代码中会在编译前报错:无法分配到 color ,因为它是只读属性

12.函数

可选参数

函数参数可以被标记为可选的,这意味着在调用函数时可以省略这个参数。要使参数成为可选参数,写法与接口的可选属性一样,在参数名后面加上一个问号?

function foo(params?: string): void {
  console.log(params)
}

13.interface 和 type 的区别

在 TypeScript 中,interface 是一种声明对象的结构,它描述了对象应该具有的属性和方法。而 type 是用来定义自定义类型的关键字,它可以表示任何类型,不仅限于对象。

14.tsconfig.json配置文件

tsconfig.json文件通过tsc --init命令生成的

"compilerOptions": {
  "incremental": true, // TS编译器在第一次编译之后会生成一个存储编译信息的文件,第二次编译会在第一次的基础上进行增量编译,可以提高编译的速度
  "tsBuildInfoFile": "./buildFile", // 增量编译文件的存储位置
  "diagnostics": true, // 打印诊断信息 
  "target": "ES5", // 目标语言的版本
  "module": "CommonJS", // 生成代码的模板标准
  "outFile": "./app.js", // 将多个相互依赖的文件生成一个文件,可以用在AMD模块中,即开启时应设置"module": "AMD",
  "lib": ["DOM", "ES2015", "ScriptHost", "ES2019.Array"], // TS需要引用的库,即声明文件,es5 默认引用dom、es5、scripthost,如需要使用es的高级版本特性,通常都需要配置,如es8的数组新特性需要引入"ES2019.Array",
  "allowJS": true, // 允许编译器编译JS,JSX文件
  "checkJs": true, // 允许在JS文件中报错,通常与allowJS一起使用
  "outDir": "./dist", // 指定输出目录
  "rootDir": "./", // 指定输出文件目录(用于输出),用于控制输出目录结构
  "declaration": true, // 生成声明文件,开启后会自动生成声明文件
  "declarationDir": "./file", // 指定生成声明文件存放目录
  "emitDeclarationOnly": true, // 只生成声明文件,而不会生成js文件
  "sourceMap": true, // 生成目标文件的sourceMap文件
  "inlineSourceMap": true, // 生成目标文件的inline SourceMap,inline SourceMap会包含在生成的js文件中
  "declarationMap": true, // 为声明文件生成sourceMap
  "typeRoots": [], // 声明文件目录,默认时node_modules/@types
  "types": [], // 加载的声明文件包
  "removeComments":true, // 删除注释 
  "noEmit": true, // 不输出文件,即编译后不会生成任何js文件
  "noEmitOnError": true, // 发送错误时不输出任何文件
  "noEmitHelpers": true, // 不生成helper函数,减小体积,需要额外安装,常配合importHelpers一起使用
  "importHelpers": true, // 通过tslib引入helper函数,文件必须是模块
  "downlevelIteration": true, // 降级遍历器实现,如果目标源是es3/5,那么遍历器会有降级的实现
  "strict": true, // 开启所有严格的类型检查
  "alwaysStrict": true, // 在代码中注入'use strict'
  "noImplicitAny": true, // 不允许隐式的any类型
  "strictNullChecks": true, // 不允许把null、undefined赋值给其他类型的变量
  "strictFunctionTypes": true, // 不允许函数参数双向协变
  "strictPropertyInitialization": true, // 类的实例属性必须初始化
  "strictBindCallApply": true, // 严格的bind/call/apply检查
  "noImplicitThis": true, // 不允许this有隐式的any类型
  "noUnusedLocals": true, // 检查只声明、未使用的局部变量(只提示不报错)
  "noUnusedParameters": true, // 检查未使用的函数参数(只提示不报错)
  "noFallthroughCasesInSwitch": true, // 防止switch语句贯穿(即如果没有break语句后面不会执行)
  "noImplicitReturns": true, //每个分支都会有返回值
  "esModuleInterop": true, // 允许export=导出,由import from 导入
  "allowUmdGlobalAccess": true, // 允许在模块中全局变量的方式访问umd模块
  "moduleResolution": "node", // 模块解析策略,ts默认用node的解析策略,即相对的方式导入
  "baseUrl": "./", // 解析非相对模块的基地址,默认是当前目录
  "paths": { // 路径映射,相对于baseUrl
    // 如使用jq时不想使用默认版本,而需要手动指定版本,可进行如下配置
    "jquery": ["node_modules/jquery/dist/jquery.min.js"]
  },
  "rootDirs": ["src","out"], // 将多个目录放在一个虚拟目录下,用于运行时,即编译后引入文件的位置可能发生变化,这也设置可以虚拟src和out在同一个目录下,不用再去改变路径也不会报错
  "listEmittedFiles": true, // 打印输出文件
  "listFiles": true// 打印编译的文件(包括引用的声明文件)
}
 
// 指定一个匹配列表(属于自动指定该路径下的所有ts相关文件)
"include": [
   "src/**/*"
],
// 指定一个排除列表(include的反向操作)
 "exclude": [
   "demo.ts"
],
// 指定哪些文件使用该配置(属于手动一个个指定文件)
 "files": [
   "demo.ts"
]

15.三斜线指令

三斜线指令是包含单个XML标签的单行注释。 注释的内容会做为编译器指令使用。

三斜线指令是包含单个XML标签的单行注释。 注释的内容会做为编译器指令使用。

三斜线指令仅可放在包含它的文件的最顶端。 一个三斜线指令的前面只能出现单行或多行注释,这包括其它的三斜线指令。 如果它们出现在一个语句或声明之后,那么它们会被当做普通的单行注释,并且不具有特殊的涵义。

/// 指令是三斜线指令中最常见的一种。 它用于声明文件间的 依赖。

三斜线引用告诉编译器在编译过程中要引入的额外的文件。

你也可以把它理解能import,它可以告诉编译器在编译过程中要引入的额外的文件

例如a.ts

namespace A {
    export const fn = () => 'a'
}

b.ts

namespace A {
    export const fn2 = () => 'b'
}

index.ts

引入之后直接可以使用变量A

///<reference path="./index2.ts" />
///<reference path="./index3.ts" />
console.log(A);

声明文件引入

例如,把 /// 引入到声明文件,表明这个文件使用了 @types/node/index.d.ts里面声明的名字; 并且,这个包需要在编译阶段与声明文件一起被包含进来。

仅当在你需要写一个d.ts文件时才使用这个指令。

/// 注意事项:

如果你在配置文件 配置了noResolve 或者自身调用自身文件会报错