TypeScript基础

TypeScript基础

1、变量

(1) 变量声明

在TypeScript中定义变量需要定义标识符类型。

声明了类型后TypeScript就会进行类型检测,声明的类型可以称之为类型注解;

自动类型判断;

  • TS拥有自动的类型判断机制
  • 当对变量的声明和赋值是同时进行的,TS编译器会自动判断变量的类型
  • 所以如果你的变量的声明和赋值时同时进行的,可以省略掉类型声明
// 完整声明格式:
var/let/const 标识符: 数据类型 = 赋值;
//例:
let name:string = 'CoderKing';
// 注:类型string是小写的,和String是不同的。
// string是TypeScript中定义的字符串类型,String是ECMAScript中定义的一个类

(2) 声明变量关键字

在TypeScript定义变量(标识符)和ES6之后一致,可以使用var、let、const来定义。(已不推荐用var)

var name:string = 'CoderKing-one';
let name:string = 'CoderKing-two';
const NAME:string = 'CoderKing-three';

2、数据类型

(1) 基本类型

类型描述
number1, -33, 2.5任意数字
string'hi', "hi", hi任意字符串
booleantrue、false布尔值true或false
字面量其本身限制变量的值就是该字面量的值
null/undefined和js一致和js一致
symbol和js一致和js一致
any*任意类型
unknown*类型安全的any
void空值(undefined)没有值(或undefined)
never没有值不能是任何值
object{name:'孙悟空'}任意的JS对象
array[1,2,3]任意JS数组
tuple[4,5]元素,TS新增类型,固定长度数组
enumenum{A, B}枚举,TS中新增类型
  • number

    let decimal: number = 6;
    let hex: number = 0xf00d;
    let binary: number = 0b1010;
    let octal: number = 0o744;
    //大数字
    let big: bigint = 100n;
    
  • boolean

    let flag: boolean = true
    flag = 20 > 30
    
  • string

    let color: string = "blue";
    color = 'red';
    
    let fullName: string = `Bob Bobbington`;
    let age: number = 37;
    let sentence: string = `Hello, my name is ${fullName}.
    
    I'll be ${age + 1} years old next month.`;
    
  • 字面量

    也可以使用字面量去指定变量的类型,通过字面量可以确定变量的取值范围

    let color: 'red' | 'blue' | 'black';
    let num: 1 | 2 | 3 | 4 | 5;
    
  • null/undefined

    let n1: null = null
    let n2: undefined = undefined
    
  • symbol

    const title1 = Symbol("title")
    const title2 = Symbol('title')
    
    const info = {
      [title1]: "程序员",
      [title2]: "老师"
    }
    
  • any

    // // 当进行一些类型断言 as any
    // 在不想给某些JavaScript添加具体的数据类型时(原生的JavaScript代码是一样)
    let d: any = 4;
    d = 'hello';
    d = true;
    
  • unknown

    function foo() { return "abc" }
    function bar() { return 123 }
    
    // unknown类型只能赋值给any和unknown类型
    // any类型可以赋值给任意类型
    let flag = true
    let result: unknown // 最好不要使用any
    
    if (flag) {
      result = foo()
    } else {
      result = bar()
    }
    
    let message: string = result
    let num: number = result
    console.log(result)
    
  • void

    function sum(num1: number, num2: number):void {
      console.log(num1 + num2)
    }
    
    sum(20, 30)
    // sum("abc", "cba")
    
    
  • never

    function handleMessage(message: string | number | boolean): never {
      switch (typeof message) {
        case 'string':
          console.log("string处理方式处理message")
          break
        case 'number':
          console.log("number处理方式处理message")
          break
        case 'boolean':
          console.log("boolean处理方式处理message")
          break
        default:
          const check: never = message
      }
    }
    
    handleMessage("abc")
    
  • object

    // 完整写法
    const info: {
      name: String;
      age: Number;
    } = {
      name: "why",
      age: 18,
    };
    
    // ts有类型推导,可不写
    const info2 = {
      name: "why",
      age: 18,
    };
    
  • array

    // 类型注解: type annotation
    let list: Array<number> = [1, 2, 3];  // 不推荐(react jsx中是有冲突   <div></div>)
    let list: number[] = [1, 2, 3]; // 推荐
    
  • tuple

    //元组的类型和长度是确定的
    const info: [string, number, number] = ["why", 18, 1.88]
    const name = info[0]
    console.log(name.length)
    

    应用场景:

    function useState(state: any) {
      let currentState = state
      const changeState = (newState: any) => {
        currentState = newState
      }
    
      const tuple: [any, (newState: any) => void] = [currentState, changeState]
      return tuple
    }
    
    const [counter, setCounter] = useState(10);
    setCounter(1000)
    
    const [title, setTitle] = useState("abc")
    
  • enum

    enum Color {
        Red,
        Green,
        Blue,
    }
    let c: Color = Color.Green;
    
    enum Color {
        Red = 1,
        Green,
        Blue,
    }
    let c: Color = Color.Green;
    
    enum Color {
        Red = 1,
        Green = 2,
        Blue = 4,
    }
    let c: Color = Color.Green;
    

(2) 类型补充

  • 函数参数和返回值类型

    // 给参数加上类型注解: num1: number, num2: number
    // 给返回值加上类型注释: (): number
    // 在开发中,通常情况下可以不写返回值的类型(自动推导)
    function sum(num1: number, num2: number) {
      return num1 + num2
    }
    
    // sum(123, 321)
    
  • 匿名函数的参数类型

    // 通常情况下, 在定义一个函数时, 都会给参数加上类型注解的
    function foo(message: string) {
    
    }
    
    const names = ["abc", "cba", "nba"]
    // item根据上下文的环境推导出来的, 这个时候可以不添加的类型注解
    // 上下文中的函数: 可以不添加类型注解
    names.forEach(function(item) {
      console.log(item.split(""))
    })
    
  • 对象类型

    // Point: x/y -> 对象类型
    // {x: number, y: number}
    function printPoint(point: {x: number, y: number}) {
      console.log(point.x);
      console.log(point.y)
    }
    
    printPoint({x: 123, y: 321})
    
  • 可选类型

    // Point: x/y/z -> 对象类型
    // {x: number, y: number, z?: number}
    function printPoint(point: {x: number, y: number, z?: number}) {
      console.log(point.x)
      console.log(point.y)
      console.log(point.z)
    }
    
    printPoint({x: 123, y: 321})
    printPoint({x: 123, y: 321, z: 111})
    
  • 联合类型

    // number|string 联合类型
    function printID(id: number|string|boolean) {
      // 使用联合类型的值时, 需要特别的小心
      // narrow: 缩小
      if (typeof id === 'string') {
        // TypeScript帮助确定id一定是string类型
        console.log(id.toUpperCase())
      } else {
        console.log(id)
      }
    }
    
    printID(123)
    printID("abc")
    printID(true)
    
  • 类型别名

    // type用于定义类型别名(type alias)
    type IDType = string | number | boolean
    type PointType = {
      x: number
      y: number
      z?: number
    }
    
    function printId(id: IDType) {}
    function printPoint(point: PointType) {}
    
  • 类型断言as

    // <img id="why"/>
    
    // 1.类型断言 as
    const el = document.getElementById("why") as HTMLImageElement;
    el.src = "url地址";
    
    // 2.另外案例: Person是Student的父类
    class Person {}
    
    class Student extends Person {
      studying() {}
    }
    
    function sayHello(p: Person) {
      (p as Student).studying();
    }
    
    const stu = new Student();
    sayHello(stu);
    
    // 3.了解: as any/unknown
    const message = "Hello World";
    // const num: number = (message as unknown) as number
    
  • 非空类型断言

    // message? -> undefined | string
    function printMessageLength(message?: string) {
      // if (message) {
      //   console.log(message.length)
      // }
      // vue3源码
      console.log(message!.length);
    }
    
    printMessageLength("aaaa");
    printMessageLength("hello world");
    
  • 可选链的使用

    type Person = {
      name: string
      friend?: {
        name: string
        age?: number,
        girlFriend?: {
          name: string
        }
      }
    }
    
    const info: Person = {
      name: "why",
      friend: {
        name: "kobe",
        girlFriend: {
          name: "lily"
        }
      }
    }
    
    // 另外一个文件中
    console.log(info.name)
    // console.log(info.friend!.name)
    console.log(info.friend?.name)
    console.log(info.friend?.age)
    console.log(info.friend?.girlFriend?.name)
    
    // if (info.friend) {
    //   console.log(info.friend.name)
    
    //   if (info.friend.age) {
    //     console.log(info.friend.age)
    //   }
    // }
    
  • !!运算符

    const message = "Hello World";
    // const message = "";
    
    // const flag = Boolean(message)
    // console.log(flag)
    
    const flag = !!message;
    console.log(flag);
    
    const flag1 = message;
    
  • ??运算符

    let message: string | null = "Hello World";
    
    const content = message ?? "你好啊, 李银河";
    // const content = message ? message: "你好啊, 李银河"
    console.log(content);
    
  • 类型缩小

    // 1.typeof的类型缩小
    type IDType = number | string;
    function printID(id: IDType) {
      if (typeof id === "string") {
        console.log(id.toUpperCase());
      } else {
        console.log(id);
      }
    }
    
    // 2.平等的类型缩小(=== == !== !=/switch)
    type Direction = "left" | "right" | "top" | "bottom";
    function printDirection(direction: Direction) {
      // 1.if判断
      // if (direction === 'left') {
      //   console.log(direction)
      // } else if ()
      // 2.switch判断
      // switch (direction) {
      //   case 'left':
      //     console.log(direction)
      //     break;
      //   case ...
      // }
    }
    
    // 3.instanceof
    function printTime(time: string | Date) {
      if (time instanceof Date) {
        console.log(time.toUTCString());
      } else {
        console.log(time);
      }
    }
    
    class Student {
      studying() {}
    }
    
    class Teacher {
      teaching() {}
    }
    
    function work(p: Student | Teacher) {
      if (p instanceof Student) {
        p.studying();
      } else {
        p.teaching();
      }
    }
    
    const stu = new Student();
    work(stu);
    
    // 4. in
    type Fish = {
      swimming: () => void;
    };
    
    type Dog = {
      running: () => void;
    };
    
    function walk(animal: Fish | Dog) {
      if ("swimming" in animal) {
        animal.swimming();
      } else {
        animal.running();
      }
    }
    
    const fish: Fish = {
      swimming() {
        console.log("swimming");
      },
    };
    
    walk(fish);
    

3、函数详解

(1) 函数的类型

// 1.函数作为参数时, 在参数中如何编写类型
function foo() {}

type FooFnType = () => void;
function bar(fn: FooFnType) {
  fn();
}

bar(foo);

// 2.定义常量时, 编写函数的类型
type AddFnType = (num1: number, num2: number) => number;
const add: AddFnType = (a1: number, a2: number) => {
  return a1 + a2;
};

(2) 参数的可选类型

// 可选类型是必须写在必选类型的后面的
// y -> undefined | number
function foo(x: number, y?: number) {}

foo(20, 30);
foo(20);

(3) 参数的默认值

// 必传参数 - 有默认值的参数 - 可选参数
function foo(y: number, x: number = 20) {
  console.log(x, y);
}

foo(30);

(4) 函数的剩余参数

// function sum(num1: number, num2: number) {
//   return num1 + num2
// }

function sum(initalNum: number, ...nums: number[]) {
  let total = initalNum;
  for (const num of nums) {
    total += num;
  }
  return total;
}

console.log(sum(20, 30));
console.log(sum(20, 30, 40));
console.log(sum(20, 30, 40, 50));

(5) this的默认推导

// this是可以被推导出来 info对象(TypeScript推导出来)
const info = {
  name: "why",
  eating() {
    console.log(this.name + " eating")
  }
}

info.eating()

(6) this的不明确的类型

type ThisType = { name: string };

function eating(this: ThisType, message: string) {
  console.log(this.name + " eating", message);
}

const info = {
  name: "why",
  eating: eating,
};

// 隐式绑定
info.eating("哈哈哈");

// 显示绑定
eating.call({ name: "kobe" }, "呵呵呵");
eating.apply({ name: "james" }, ["嘿嘿嘿"]);

(7) 函数的重载

  • 联合类型

    /**
     * 通过联合类型有两个缺点:
     *  1.进行很多的逻辑判断(类型缩小)
     *  2.返回值的类型依然是不能确定
     */
    function add(a1: number | string, a2: number | string) {
      if (typeof a1 === "number" && typeof a2 === "number") {
        return a1 + a2;
      } else if (typeof a1 === "string" && typeof a2 === "string") {
        return a1 + a2;
      }
    
      // return a1 + a2;
    }
    
    add(10, 20);
    
  • 函数重载

    // 函数的重载: 函数的名称相同, 但是参数不同的几个函数, 就是函数的重载
    function add(num1: number, num2: number): number; // 没函数体
    function add(num1: string, num2: string): string;
    
    function add(num1: any, num2: any): any {
      if (typeof num1 === "string" && typeof num2 === "string") {
        return num1.length + num2.length;
      }
      return num1 + num2;
    }
    
    const result = add(20, 30);
    const result2 = add("abc", "cba");
    console.log(result);
    console.log(result2);
    console.log(console.log);
    
    // 在函数的重载中, 实现函数是不能直接被调用的
    // add({name: "why"}, {age: 18})