ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

TypeScript学习笔记

2022-07-26 15:05:36  阅读:153  来源: 互联网

标签:TypeScript string true number 笔记 学习 let 类型 type


参考文章:

TypeScript是JavaScript的超集,因为它扩展了JavaScript的语法。TypeScript就是为了做语法检查,提早发现错误,所以「类型」是其最核心的特性。

一、安装

执行全局安装:

npm install -g typescript

安装ts-node(用来构造运行环境):

npm install -g ts-node

初始化tsconfig.json(这是ts的配置文件,用来指定ts的编译规则):

tsc --init

二、tsconfig.json介绍

这是一个常用的tsconfig.json示例,ts的配置选项有很多,用法直接百度即可

{
    "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, // 开启所有严格的类型检查
  "jsx": "preserve", // 指定 jsx 格式
  "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的网站:https://www.typescriptlang.org/zh/play 学习ts时,可以在线练习加深记忆与理解。

1.基础数据类型

JS八种内置类型(含ES6新增的)与TS对应的类型,基本上一样

JS类型 对应TS声明语法
字符串(string) let name: string = "bob";
数字(number) let age:number = 18;
布尔值(boolean) let flag:boolean = false;
空值(null) let n:null = null;
未定义(undefined) let u:undefined = undefined;
对象(object) let obj: object = {x: 1};
大整数(bigint, ES6新增) let bigLiteral: bigint = 100n;
符号(symbol,ES6新增) let sym: symbol = Symbol("me");

注意点:

  • null和undefined是所有类型的子类型。假设有这个么一个场景:

    let name = person.name || undefined;
    

    person的取值有可能是空的,这时可以用undefined指定可能出现的值,不然TS编译不过去。

    null和undefined也可相互赋值:

    let str: string = 'hello';
    str = null; // OK
    str = undefined; // OK
    
    let a: null = undefined; // OK
    let b: undefined = null; // OK
    
  • 用object去声明类型一般不会这么去做,因为这样也会被ts警告不存在此类型,一般用interface指定显示声明

    interface Person {
        name: string,
        age: number
    }
    
    let p1:Person = { name:'xiaoming', age: 20 }
    
  • 【TS独有类型】void类型:表示没有任何类型,不能直接赋值。

    let a: void;
    let b: number = a; // Error
    

    这个类型是用来定义函数返回值的,假如你的函数没有返回值

    function fun(): void {
      console.log("this is TypeScript");
    };
    
  • 【TS独有类型】any 和 unknown

    any是ts毁灭者,用不好会导致“anyscript”。any会跳过类型检查器对值的检查,任何值都可以赋值给any类型。除非一个对象的类型实在难以定义,迫不得己时用any,大多数时候尽量不要用

    let notSure: any = 4;
    notSure = "maybe a string instead"; // OK
    notSure = false; // OK
    

    unknown表示未知类型,他与any的区别就是,你虽然赋值给了unknown,但是你在使用这个变量的时候必须得进行类型检查,例如:

    function getDogName() {
     let x: unknown;
     return x;
    };
    
    const dogName = getDogName();
    
    // 直接使用
    const upName = dogName.toLowerCase(); // Error
     
    // typeof
    if (typeof dogName === 'string') {
      const upName = dogName.toLowerCase(); // OK
    }
    
    // 类型断言 
    const upName = (dogName as string).toLowerCase(); // OK
    

    这种机制起到了很强的预防性,更安全,这就要求我们必须缩小类型,我们可以使用typeof类型断言等方式来缩小未知范围

  • 【TS独有类型】never类型

    never类型表示的是那些永不存在的值的类型。

    值会永不存在的两种情况:

    1.如果一个函数执行时抛出了异常,那么这个函数永远不存在返回值(因为抛出异常会直接中断程序运行,这使得程序运行不到返回值那一步,即具有不可达的终点,也就永不存在返回了);

    2.函数中执行无限循环的代码(死循环),使得程序永远无法运行到函数返回值那一步,永不存在返回。

    // 异常
    function err(msg: string): never { // OK
      throw new Error(msg); 
    }
    
    // 死循环
    function loopForever(): never { // OK
      while (true) {};
    }
    
2.类型断言

类型断言指的是你可以手动给用到的变量断定一个类型,这意味你编写的代码的时候比TS清楚知道这个变量它就是这个类型,毕竟TS智能程度还是有限的。

类型断言有两种写法:

let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
let strLength: number = (someValue as string).length;
3.联合类型

联合类型表示取值可以为多种类型中的一种,使用 | 分隔每个类型。

let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven'; // OK
myFavoriteNumber = 7; // OK
4.类型推断

在 TypeScript 中,具有初始化值的变量、有默认值的函数参数、函数返回的类型都可以根据上下文推断出来。比如我们能根据 return 语句推断函数返回的类型,如下代码所示:

 /** 根据参数的类型,推断出返回值的类型也是 number */
  function add1(a: number, b: number) {
    return a + b;
  }

如果定义的时候没有赋值,不管之后有没有赋值,都会被推断成 any 类型而完全不被类型检查:

let myFavoriteNumber;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
5.接口

接口用来描述对象行为,就是描述了该对象包含什么属性,以及函数的参数类型,返回值等等。

interface Person {
    id: string,
    name: string,
    age: number
}

function addPerson(person: Person): boolean {
    console.log('添加了一个人',person);
    return true
}

function updatePerson(person: { id: string, name: string, age: number })

// 调用
addPerson({ id: '1', name: 'xiaoming', age: 18, hobby:['唱','跳','rap'] })

​ 上面的代码给出了接口的使用示例,规定了函数入参类型,以及返回值类型。updatePerson方法,函数传参需严格按照对象传参,且不能多出额外字段,也不能少字段,否则会被TS警告编译不通过。但是addPerson方法却能传递多余的参数。这是因为:TypeScript 的核心原则之一是对值所具有的结构进行类型检查。 它有时被称做鸭式辨型法或结构性子类型化。

​ 所谓的鸭式辨型法就是像鸭子一样走路并且嘎嘎叫的就叫鸭子,即具有鸭子特征的认为它就是鸭子,也就是通过制定规则来判定对象是否实现这个接口。所以,在参数中直接写对象,相当于严格进行类型定义,直接给person赋值,不能多参或者少参。而通过外部接口定义,并进行赋值。Ts将会进行类型推断,只要规定的参数出现即可,故而可以用此法来绕开多余的类型检查。

小提示:在 type、interface 中可以使用逗号、分号分隔符

接口的可选属性

选属性就是在可选属性名字定义的后面加一个?符号,来证明该属性是可有可无的。

interface Person { 
  name: string; 
  age: number; 
  money?: number;
}
只读属性

在属性名前用readonly关键字来指定只读属性,该对象属性只能在对象刚刚创建的时候修改其值

interface Point {
  readonly x: number;
  readonly y: number;
}

let p: Point = { x: 10, y: 20 };
p.x = 5; // Error
绕开额外属性检查的方式

1.通过鸭式辨别法绕过,上面例子已经说明

2.使用类型断言:类型断言的意义就等同于你在告诉程序,你很清楚自己在做什么,此时程序自然就不会再进行额外的属性检查了。

interface Person { 
  name: string; 
  age: number; 
  money?: number;
}

let p: Person = {
  name: "xiaoming",
  age: 18,
  money: 100,
  girl: false
} as Person; // OK

3.索引签名

interface Person { 
  name: string; 
  age: number; 
  money?: number;
  [key: string]: any;// 可以添加任意属性
}

let p: Person = {
  name: "兔神",
  age: 25,
  money: -100000,
  girl: false
}; 
接口继承

接口继承接口使用关键字extends, 继承的本质是复制,抽出共同的代码,所以子接口拥有父接口的类型

interface Shape {
  color: string;
}
interface Square extends Shape {
  sideLength: number;
}

let square: Square = { sideLength: 1 }; // Error
let square1: Square = { sideLength: 1, color: 'red' }; // OK

多继承

interface Shape {
  color: string;
}
interface PenStroke {
  penWidth: number;
}
interface Square extends Shape, PenStroke {
  sideLength: number;
}

注意:若多继承的两个或多个父接口有相同属性,但定义的类型不同,TS会直接报错

interface Shape {
  name: string;
  color: string;
}
interface PenStroke {
  name: number;
  penWidth: number;
}
interface Square extends Shape, PenStroke { // Error
  sideLength: number;
}
6.函数类型
声明函数
  • 在Ts中声明一个函数
function sum(x: number, y: number): number {
    return x + y;
}
  • 函数表达式方式
let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
    return x + y;
};

TypeScript 的类型定义中,=> 用来表示函数的定义,左边是输入类型,需要用括号括起来,右边是输出类型。切忌与ES6的箭头函数混淆了。

  • 用接口定义函数类型
interface SearchFunc{
  (source: string, subString: string): boolean;
}

let mySearch: SearchFunc = function(source: string, subString: string) { // OK
  let result = source.search(subString);
  return result >-1;
};
可选参数
function buildName(firstName: string, lastName?: string) {
    if (lastName) {
        return firstName + ' ' + lastName;
    } else {
        return firstName;
    }
}
let tomcat = buildName('Tom', 'Cat');
let tom = buildName('Tom');

注意:可选参数后面不允许再出现必需参数

参数默认值
function buildName(firstName: string, lastName: string = 'Cat') {
    return firstName + ' ' + lastName;
}
let tom = buildName('Tom');
剩余参数
function push(array: any[], ...items: any[]) {
    items.forEach(function(item) {
        array.push(item);
    });
}

let a = [];
push(a, 1, 2, 3);
7.类型别名

类型别名,仅仅是给类型取了一个新的名字,并不是创建了一个新的类型

type Message = string | string[];
let greet = (message: Message) => {
  // ...
};
type StringType = string;
let str: StringType;
str = 'hello';
str = 123 // Error

字符串字面量类型用来约束取值只能是某几个字符串中的一个

type Name = 'ALisa' | 'Bob' | 'Cola'

let name1: Name = 'ALisa'; // OK
let name2: Name = 'Bob'; // OK
let name3: Name = 'Cola'; // OK
let name4: Name = '兔兔'; // Error

type实现继承,可以使用交叉类型type A = B & C & D

type IntersectionType = { id: number; name: string; } & { age: number };
const mixed: IntersectionType = {
    id: 1,
    name: 'name',
    age: 18
}
8.泛型
泛型定义

泛型是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性

function identity<T>(arg: T): T {
    return arg;
}

identity('stringType')

泛型T在调用的时候TS会自动推断类型。

一次定义多个类型参数:

function swap<T, U>(tuple: [T, U]): [U, T] {
    return [tuple[1], tuple[0]];
}

swap([7, 'seven']); // ['seven', 7]
泛型约束

在函数内部使用泛型变量的时候,由于事先不知道它是哪种类型,所以不能随意的操作它的属性或方法

function loggingIdentity<T>(arg: T): T {
    console.log(arg.length); // Error
    return arg;
}

使用接口来解决这个问题:

interface Lengthwise {
    length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);  // OK
    return arg;
}

loggingIdentity({length: 10, value: 3}); // OK
loggingIdentity([1,2]); // OK
泛型接口
interface Person<T> {
    name: T;
    getAge(arg: T): T;
}

let myIdentity: Person<string> = {
    name: "兔兔",
    getAge(name) {
        return name
    }
};
泛型类
class GenericNumber<T> {
    zeroValue: T;
    add: (x: T, y: T) => T;
}

let myGenericNumber = new GenericNumber<number>();

注意:静态属性不能使用泛型static zeroValue: T; // Error

9.类型检测
typeof

typeof 操作符可以用来获取一个变量或对象的类型

interface Hero {
  name: string;
  skill: string;
}

const zed: Hero = { name: "影流之主", skill: "影子" };
type LOL = typeof zed; // type LOL = Hero

在上面代码中,我们通过 typeof 操作符获取 zed 变量的类型并赋值给 LOL 类型变量,之后我们就可以使用 LOL 类型

const ahri: LOL = { name: "大司马", skill: "我黑切呢" };
keyof

keyof 与 Object.keys 略有相似,只不过 keyof 取 interface 的键

interface Point {
    x: number;
    y: number;
}

// type keys = "x" | "y"
type keys = keyof Point;

JavaScript 是一种高度动态的语言。有时在静态类型系统中捕获某些操作的语义可能会很棘手。以一个简单的prop 函数为例:

function prop(obj, key) {
  return obj[key];
}

该函数接收 obj 和 key 两个参数,并返回对应属性的值。对象上的不同属性,可以具有完全不同的类型,我们甚至不知道 obj 对象长什么样。

假设你用TS改写后

function prop(obj: object, key: string) {
  return obj[key];
}
// Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{}'

元素隐式地拥有 any 类型,因为 string 类型不能被用于索引 {} 类型。

此时你可能想到,我既然不知道obj, key的具体类型,那么用泛型解决好了:

function prop<T,K>(obj: T, key: K) {
    return obj[key];
}

// Type 'K' cannot be used to index type 'T'

在T类型上不能使用索引K,因为T类型有可能是任何类型,K也是,所以按照思路,应该对T,K都进行取值限制。

function prop<T extends object, K extends keyof T>(obj: T, key: K) {
  return obj[key];
}

在以上代码中,我们使用了 TypeScript 的泛型和泛型约束。首先定义了 T 类型并使用 extends 关键字约束该类型必须是 object 类型的子类型,然后使用 keyof 操作符获取 T 类型的所有键,其返回类型是联合类型,最后利用 extends 关键字约束 K 类型必须为 keyof T 联合类型的子类型。

进行测试:

interface Todo {
    id: number;
    text: string;
    done: boolean;
}

const todo:Todo = {
    id: 1,
    text: 'learn typescript',
    done: false
}

function prop<T extends object,K extends keyof T>(obj: T, key: K){
    return obj[key]
}

const id = prop(todo, "id"); // const id: number
const text = prop(todo, "text"); // const text: string
const done = prop(todo, "done"); // const done: boolean

console.log(id,text,done);
// 1, learn typescript, false
in

用来遍历枚举类型:

type Keys = "a" | "b" | "c"

type Obj =  {
  [p in Keys]: any
} // -> { a: any, b: any, c: any }
10.TS常见的工具类型
Partial<T>

将T中所有属性转换为可选属性。返回的类型可以是T的任意子集

interface UserModel {
  name: string;
  age: number;
  sex: number;
}
type JUserModel = Partial<UserModel>

// 实际上等同于
type JUserModel = {
    name?: string | undefined;
    age?: number | undefined;
    sex?: number | undefined;
}
Required<T>

与Partial相反,属性都是必须的

type JUserModel2 = Required<UserModel>
// 等同于
type JUserModel2 = {
    name: string;
    age: number;
    sex: number;
}
Readonly<T>

将属性设置为只读的,声明变量只能在初次赋值

interface Todo {
 title: string;
}

const todo: Readonly<Todo> = {
 title: "Delete inactive users"
};

todo.title = "Hello"; // Error: cannot reassign a readonly property
Record<K,T>

将泛型为K的对象中每个键转换成T类型

interface PageInfo {
  title: string;
}

type Page = "home" | "about" | "contact";

const x: Record<Page, PageInfo> = {
  about: { title: "about" },
  contact: { title: "contact" },
  home: { title: "home" },
};
Pick<T,K>

在一个声明好的对象中,挑选一部分出来组成一个新的声明对象

interface Todo {
  title: string;
  description: string;
  done: boolean;
}

type TodoBase = Pick<Todo, "title" | "done">;

// 等同于
type TodoBase = {
    title: string;
    done: boolean;
}
Omit<T,K>

从T中取出除去K的其他所有属性。与Pick相对

interface Todo {
  title: string;
  description: string;
  done: boolean;
}

type TodoBase = Omit<Todo, "description">;

// 等同于
type TodoBase = {
    title: string;
    done: boolean;
}
Exclude<T,U>

从T中排除可分配给U的属性,剩余的属性构成新的类型

type T0 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
type T1 = Exclude<"a" | "b" | "c", "a" | "b">; // "c"
type T2 = Exclude<string | number | (() => void), Function>; // string | number
NonNullable<T>

去除T中的 null 和 undefined 类型

type T0 = NonNullable<string | number | undefined>; // string | number
type T1 = NonNullable<string[] | null | undefined>; // string[]
Parameters<T>

获取函数的参数类型,返回值是数组

type A = Parameters<() =>void>; // []
type B = Parameters<typeof Array.isArray>; // [any]
type C = Parameters<typeof parseInt>; // [string, (number | undefined)?]
type D = Parameters<typeof Math.max>; // number[]
ReturnType<T>

获取函数的返回值类型

type T0 = ReturnType<() => string>;  // string

type T1 = ReturnType<(s: string) => void>;  // void

标签:TypeScript,string,true,number,笔记,学习,let,类型,type
来源: https://www.cnblogs.com/suanyunyan/p/16521030.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有