一份不可多得的TypeScript系统入门整理2

程序员哆啦A梦

TypeScript中的配置文件

// 编译配置文件
// tsconfig.json
要编译的文件
"include" ["./demo.ts"],

联合类型和类型保护

interface Bird {
 fly: boolean;
 sing: ()=>{};
}
interface Dog {
 fly: boolean;
 bark: ()=>{};
}
// 类型断言的方式
function trainAnial(animal: Bird | Dog) {
 if(animal.fly) {
  (animal as Bird).sing();
 } else {
  (animal as Dog).bark();
 }
}

// in 语法来做类型保护
function trainAnialSecond(animal: Bird | Dog) {
 if('sing' in animal) {
  animal.sing();
 } else {
  animal.bark();
 }
}
// typeof 语法来做类型保护
function add(first: string | number, second: string | number) {
 if(typeof first === 'string' || typeof second === 'string') {
  return `${first}${second}`;
 }
 return first + second;
}

// 使用instanceof语法来做类型保护
class NumberObj {
 count: number;
}

function addSecond(first: object | NumberObj, second: object | NumberObj) {
 if(first instanceof NumberObj && second instanceof NumberObj) {
  return first.count + second.count;
 }
 return 0;
}

Enum枚举类型

const Status = {
 OFFLINE: 0,
 ONLINE: 1,
 DELETED: 2
}
function getResult(status) {
 if(status === Status.OFFLINE){
  return 'offline';
 }else if(status === Status.ONLINE) {
  return 'online';
 }else if(status === Status.DELETED) {
  return 'deleted';
 }
 return 'error';
}
enum Status {
 OFFLINE,
 ONLINE,
 DELETED2
}
function getResult(status) {
 if(status === Status.OFFLINE){
  return 'offline';
 }else if(status === Status.ONLINE) {
  return 'online';
 }else if(status === Status.DELETED) {
  return 'deleted';
 }
 return 'error';
}

函数泛型

// 泛型generic泛指的 类型
function join<T,P>(first: T, second: P) {
 return `${first}${second}`;
}
function anotherJoin<T>(first: T,second: T): T {
 return first;
}

// T[]
function map<T>(params: Array<T>) {
 return params;
}
// join<number,string>(1,'1');
// map<string>(['123']);
join(1,'1');

类中如何使用泛型

interface Item {
 name: string;
}
class DataManager<T extends Item> {
 constructor(private data: T[]) {}
 getItem(index: number):string {
  return this.data[index].name;
 }
}

const data = new DataManager({
 {
  name: 'jeskson'
 }
]};

// 用泛型可以声明一些类型:
// 如何使用泛型作为一个具体的类型注解
function hello<T>(params: T) {
 return params;
}
const func: <T>(param: T) => T = hello;

命名空间

"use strict"
var Header = (function() {
 function Header() {
  var elem = document.createElement('div');
  elem.innerText = 'This is Header';
  document.body.appendChild(elem);
 }
 return Header;
}());

var Content = (function()=>{
 function Content() {
  var elem = document.createElement('div');
  elem.innerText = 'This is Content';
  document.body.appendChild(elem);
 }
 return Content
}());

使用Parcel打包TS代码

yarn add --dev parcel@next

泛型中keyof语法的使用

某一数据类型的key的数组集合,既适用于数组,也适用于对象

interface testInter {
    name: string,
    age: number
}
let testArr: string[] = ['dada', 'dada1'];
let testObj: testInter = {name: 'tate', age: 26}

// 数组
function showKey<K extends keyof T, T> (key: K, obj: Array<string>) {
    return key;
}
showKey<number, Array<string>>(1, testArr);

// 对象
function showKey<K extends keyof T, T> (keyItem: K, obj: T): K {
    return keyItem;
}
let val = showKey('name', testObj)

function showKey<K extends keyof T, T> (items: K[], obj: T): T[K][] {
    return items.map(item => obj[item])
}
interface Person {
 name: string;
 age: number;
 gender: string;
}
class Teacher {
 constructor(private info: Person) {}
 getInfo(key: string) {
  if(key==='name' || key==='age' || key==='gender') {
   return this.info[key];
  }
 }
}

const teacher = new Teacher({
 name: 'jeskson',
 age: 12,
 gender: 'male'
});
const test = teacher.genInfo('name');
class Teacher {
 constructor(private info: Person) {}
 // getInfo<T extends keyof Person>(key:string) {
 getInfo<T extends keyof Person>(key: T):Person[T]{
  return this.info[key];
 }
}

装饰器

// 类的装饰器
// 装饰器本身是一个函数
// 装饰器通过@符号来使用
// 普通方法,target对应的是类的prototype
// 静态方法,target对应的是类的构造函数
function getNameDecorator(target:any,key:string){
 console.log(target,key);
}
class Test {
 name: string;
 constructor(name: string){
  this.name = name;
 }
 @getNameDecorator
 static getName() {
  return '123';
 }
}

接口Interface

有时候我们传入的参数可能会包含很多的属性,但编译器只会检查那些必须的属性是否存在,以及类型是否匹配,而接口就是用来描述这样的结构。

function Person(config: {name:string,age:number}) {
 console.log(config.name+config.age);
}
console.log(Person({name:'魔王哪吒',age:12}));
// 重构
interface Config {
 name: string;
 age: number;
}
function Person(config: Config) {
 console.log(config.name+config.age);
}
// 接口类型检查会检测属性有没有在Config接口中而进行限制

可选属性

接口中的属性有时候是不必须的,有的用得到,有的用不到的情况下,是可选属性,这样对可能存在的属性进行预先定义。

interface Config {
 name: string;
 age?: number;
 // [propName: string]: any 转字符串索引签名
}
// [propName: string]: any
// 这个索引签名是为了你能够预见某个对象可能有某些特殊的用途而准备的
// 属性名写错,可以通过索引签名的方式进行屏蔽错误

只读属性

对于一些对象属性只能在对象刚刚创建的时候修改其值,在属性前用readonly来指定只读属性:

interface Point {
  readonly x: number;
  readonly y: number;
}
let p:Point = { x: 12, y: 14 }
p.x = 15 // 错误

函数类型

接口能够描述JavaScript中对象拥有的各种各样的外形

函数类型接口:

interface Fun {
 (source: string, subString: string): Boolean
}

接口继承

接口是可以相互继承的,能够从一个接口里复制成员到另一个接口里。

interface Animal {
 name: string;
 say(): void;
}
interface Person extends Animal {
 work(): void;
 closer: string;
}
class Pro implements Person {
 closer: string;
 name: string;
 say(): void {

 }
 work(): void {

 }
 constructor(name:string, closer:string) {
  this.name = name;
  this.closer = closer;
 }
}
let g:Person = new Pro("jeskson","it");
g.say();
g.work();

接口的定义方式:使用interface关键字

接口中可定义:

  1. 确定属性
interface UserInfo {
 name: string;
 age: number;
}

const myInfo: UserInfo = {
 name: '魔王哪吒',
 age: 12
}

接口中约束好的确定属性,定义对象变量的时候,不能少,也不能多🙅‍

  1. 可选属性
interface UserInfo {
 name: string;
 age: number;
 sex?: string;
}

const myInfo: UserInfo = {
 name: '魔王哪吒',
 age: 12
}

接口中的可选属性,是表示在对象变量中可以不存在

  1. 任意属性
interface UserInfo {
 name: string;
 age: number;
 sex?: string;
 [proName: string]: any;
}

const myInfo: UserInfo = {
  name: "dadaqianduan",
  age: 12,
  test1: '123',
  test2: 'abc',
  test3: 123
};

一旦定义了任意属性,那么确定属性和可选属性的类型都必须是任意属性类型的子类,定义了任意属性后,对象变量中的属性个数才可以出现比接口的属性数量多的情况。

  1. 只读属性
interface UserInfo {
  readonly id: number;
  name: string;
  age: number;
  sex?: string;
  [propName: string]: any;
}
const myInfo: UserInfo = {
  id: 1,
  name: "dada",
  age: 12,
  test1: "123",
  test2: "abc",
  test3: 123
};

只读属性,也是确定属性,在对象变量定义的时候必须有值,后面不能修改

interface ResponseData {
    resCode: number;
    resData: ResultData[];
    message: string;
}

interface ResultData {
    productId: number;
    productName: string;
}

let resultData = {
    resCode: 0,
    resData: [
        { productId: 1, productName:"TypeScipt实战" },
        { productId: 2, productName:"TypeScipt从入门到精通" },
    ],
    message: "success"
}

function render(res: ResponseData) {
    console.log(res.resCode, res.message)
    res.resData.forEach((obj) => {
        console.log(obj.productId, obj.productName)
    })
}

render(resultData);

只要传入的对象满足接口的必要条件就可以被允许,即使传入多余的字段也可以通过类型检查

  1. 将对象字面量赋值给一个变量
let result = {
    resCode: 0,
    resData: [
        { productId: 1, productName:"TypeScipt实战", remark: "备注"},
        { productId: 2, productName:"TypeScipt从入门到精通" },
    ],
    message: "success"
}
render(result)
  1. 使用类型断言

使用类型断言方式,明确告诉编译器类型是什么,编译器就会绕过类型检查

render({
    resCode: 0,
    resData: [
        { productId: 1, productName:"TypeScipt实战", remark:""},
        { productId: 2, productName:"TypeScipt从入门到精通" },
    ],
    message: "success"
} as ResponseData)

render(<ResponseData>{
    resCode: 0,
    resData: [
        { productId: 1, productName:"TypeScipt实战", remark: "备注"},
        { productId: 2, productName:"TypeScipt从入门到精通" },
    ],
    message: "success"
})
  1. 使用字符串索引签名
interface ResultData {
    productId: number;
    productName: string;
    [remark: string]: any;  // 字符串索引签名
}

函数接口

  1. 在TS中,使用一个变量直接定义函数
let add: (x: number, y: number) => number
= (x, y){
  return x+y;
};
add(1,2)
  1. 使用接口定义函数
interface Add {
    (x: number, y: number): number
}
let myFunc: Add = function(x, y){
  return x+y;
};
myFunc(1,2);
  1. 使用类型别名来定义函数

类型别名使用type关键字

type Add = (x: number, y: number) => number

可索引类型的接口

// 数字索引接口
interface numberIndex {
    [x: number]: string
}
// 相当于声明了一个字符串类型的数组
let chars: numberIndex = ['A', 'B']
// 声明一个字符串索引类型的接口
interface stringIndex {
    [x: string]: string
}
// 两种索引签名混用
interface stringIndex {
    [x: string]: string
    [z: number]: number    // // Numeric index type 'number' is not assignable to string index type 'string'.
}

interface stringIndex {
    [x: string]: any
    [z: number]: number // Numeric index type 'number' is not assignable to string index type 'string'.
}

上手TypeScipt

对于npm的用户

npm install -g typescript

构建第一个TypeScript文件,dada.ts 文件:

function dada(person) {
 return "hello" + person;
}
let user = "jeskson";
document.body.innerHTML = dada(uer);

编译代码

在命令行上,运行TypeScript编译器:

tsc dada.ts

添加类型注解: string

function dada(person: string) {
 return "jeskson"+person;
}
let user = "jeskson";
document.body.innerHTML = dada(user);

类型注解

TypeScript里的类型注解是一种轻量级的为函数或变量添加约束的方式。

接口

允许我们在实现接口的时候只要保证包含了接口要求的结构就可以

// implements语句
interface Person {
 firstName: string;
 lastName: string;
}

function func(peson: Person) {
 return person.firstName + person.lastName;
}

let user = { firstName: "jeskson", lastName: "User" };

document.body.innerHTML = func(user);

类,支持基于类的面向对象编程

class Student {
 fullName: string;
 constructor(public firstName: string, public lastName: string) {
  this.fullName = firstName + lastName;
 }
}

interface Person {
 firstName: string;
 lastName: string;
}

function dada(person: Person) {
 return person.firstName+person.lastName;
}

let user = new Student("jeskson","魔王哪吒");
document.body.innerHTML = dada(user);

运行TypeScript Web应用

index.html里输入内容:

<!DOCTYPE html>
<html>
    <head><title>TypeScript dada</title></head>
    <body>
        <script src="dada.js"></script>
    </body>
</html>

对象

在JS中,可以任意修改对象属性,TS中不允许

// 这是因为,仅声明了对象obj的类型注解是object
let obj: object = {x: 'a', y: 'b'}
obj.x = 3    // Property 'x' does not exist on type 'object'.
let obj: {x: string, y: string} = {x: 'a', y: 'b'}
obj.x = 'c'

Symbol

具有唯一的值,可以显式声明,也可直接创建

let symbol1: Symbol = Symbol()  // 显示声明
let symbol2 = Symbol()  // 直接创建

// 验证:是否是同一个对象
console.log(symbol1 === symbol2)    // fasle

undefined 和 null

// 一旦声明了undefined,就不能再被赋值为任何其他的数据类型了
let udf: undefined = undefined
let nu: null = null

let undf: undefined = 1 
// Type '1' is not assignable to type 'undefined'.

// 默认情况下,undefined和null也不能被赋值给任何其他类型

let num1: number = undefined    
// Type 'undefined' is not assignable to type 'number'.

let num2: number = null 
// Type 'null' is not assignable to type 'number'.

打开tsconfig.js,将strictNullChecks = false(默认true)

void,any,never

// void
let voidFunc = () => {}
// 函数抛出异常,永远不会有返回值,类型为never
let error = () => {
    throw new Error('error')
}

// 死循环函数永远没有返回值,类型为never
let endless = () => {
    while(true) {}
}

对数组中的对象按对象的值进行去重

let listData = [
  { firstName: "dada", lastName: "abc", size: 18 }
}
//js
let obj = {};
listData = listData.reduce((item, next) => {
  if (!obj[next.lastName]) {
    item.push(next);
    obj[next.lastName] = true;
  }
  return item;
}, []);
//ts
const obj: {[key: string]: boolean;} = {};
listData = listData.reduce<ListDataItem[]>((item, next) => {
  if (!obj[next.lastName]) {
    item.push(next);
    obj[next.lastName] = true;
  }
  return item;
},[]);

模块的概念

“内部模块”现在称为“命令空间”,“外部模块”现在简称为“模块”,模块字其自身的作用域里执行,而不是在全局作用域里。

这意味着定义在一个模块里的变量,函数,类等等在模块外部是不可见的,除非你明确地使用export形式之一导出它们。

相反,如果想使用其它模块导出的变量,函数,类,接口等的时候,你必须要导入它们,可以使用 import 形式之一。

模块的概念:

我们可以把一些公共的功能单独抽离成一个文件作为一个模块,模块里面的变量,函数,类等默认是私有的,如果我们要在外部访问模块里面的数据,我们需要通过export暴露模块里面的数据。暴露后使用import引入模块就可以使用模块里面暴露的数据。

命名空间

命名空间和模块的区别

namespace A {
interface Animal {
 name: string;
 eat(): void;
}
class Dog implements Animal {
 name: string;
 constructor(theName: string) {
  this.name = theName;
 }
 eat() {
  console.log('dog');
 }
}
class Cat implements Animal {
 name: string;
 constructor(theName: string) {
  this.name = theName;
 }
 eat() {
  console.log('cat');
 }
}
let dog = new Dog('dogdog');
dog.eat();
}
import {A,B} from './modules/animal';

var dog = new A.Dog('hei');
dog.eat();

装饰器

装饰器是一种特殊类型的声明,它能够被附加到类声明,方法,属性或参数上,可以修改类的行为。

通俗的讲装饰器就是一个方法,可以注入到类,方法,属性参数上扩展类,属性,方法,参数的功能。

常见的装饰器有:类装饰器,属性装饰器,方法装饰器,参数装饰器

装饰器的写法:

方法参数装饰器:

参数装饰器表达式会在运行时当作函数被调用,可以使用参数装饰器为类的原型增加一些元素数据,传入下列3个参数:

function logParams(params:any){
 return function(target:any,methodName:any,paramsIndex:any){
  console.log(params);
  console.log(target);
  console.log(methodName);
  console.log(paramsIndex);
 }
}
class HttpClient{
 public url:any|undefined;
 constructor(){}
 getDate(@logParams('xxx') uuid:any){
  console.log(uuid);
 }
}

keyof

keyof与Object.keys相似,keyof取interface的键
interface Point {
 x: number;
 y: number;
}
// type keys = "x" | "y"
type keys = keyof Point;
// 无法确认返回类型
// 无法对 key 做约束
const data = {
 a: 1,
 b: 2
}

function get(o: object, name: string) {
 return o[name]
}

使用keyof:

function get<T extends object, K extends keyof T>(o: T, name: K): T[K] {
  return o[name]
}

?: 运算符

T extends U ? X : Y

type isTrue<T> = T extends true ? true : false

tsconfig.json

tsconfig.json文件中指定了用来编译这个项目的根文件和编译选项

tsconfig.json示例文件:

//"compilerOptions"可以被忽略,这时编译器会使用默认值。
//使用"files"属性
//"files"指定一个包含相对或绝对文件路径的列表。
{
    "compilerOptions": {
        "module": "commonjs",
        "noImplicitAny": true,
        "removeComments": true,
        "preserveConstEnums": true,
        "sourceMap": true
    },
    "files": [
        "core.ts",
        "sys.ts",
        "types.ts",
        "scanner.ts",
        "parser.ts",
        "utilities.ts",
        "binder.ts",
        "checker.ts",
        "emitter.ts",
        "program.ts",
        "commandLineParser.ts",
        "tsc.ts",
        "diagnosticInformationMap.generated.ts"
    ]
}
//使用"include"和"exclude"属性
//如果"files"和"include"都没有被指定,编译器默认包含当前目录和子目录下所有的TypeScript文件
//排除在"exclude"里指定的文件
{
    "compilerOptions": {
        "module": "system",
        "noImplicitAny": true,
        "removeComments": true,
        "preserveConstEnums": true,
        "outFile": "../../built/local/tsc.js",
        "sourceMap": true
    },
    "include": [
        "src/**/*"
    ],
    "exclude": [
        "node_modules",
        "**/*.spec.ts"
    ]
}

查看模式: http://json.schemastore.org/tsconfig.

回看笔者往期高赞文章,也许能收获更多喔!

❤️关注+点赞+收藏+评论+转发❤️

点赞、收藏和评论

我是Jeskson(达达前端),感谢各位人才的:点赞、收藏和评论,我们下期见!(如本文内容有地方讲解有误,欢迎指出☞谢谢,一起学习了)

我们下期见!

文章持续更新,可以微信搜一搜「 程序员哆啦A梦 」第一时间阅读,回复【资料】有我准备的一线大厂资料,本文 https://www.1024bibi.com 已经收录

github收录,欢迎Starhttps://github.com/webVueBlog/WebFamily