TypeScript学习篇-类型介绍使用、ts相关面试题


执行ts文件命令: tsc 文件.ts

基础知识

基础类型: number, string, boolean, object, array, undefined, void(代表该函数没有返回值)

类型关键字描述
任意类型any声明为 any 的变量可以赋予任意类型的值。
数字类型number双精度64位浮点值, 它可以用来表示整数和分数:
let num: number = 666;
字符串类型stringlet str: string = '呀哈哈';
布尔类型booleanlet flag: boolean = true;
数组类型声明变量为数组
在元素类型后面加[]: let arr: number[] = [1,2];,
或使用数组泛行: let arr:Array<number> = [1,2];
元组元组类型用来表示已知元素数量和类型的数组, 各元素的类型不必相同, 对应位置的类型需要相同:
let x:[string,number];
正确: x = ['呀哈哈', 2];
错误: x = [2, '呀哈哈'];
枚举enum枚举类型用于定义数值集合
enum Color {Red, Green, Blue};
let c : Color = Color.Blue;
console.log(c); // 2
voidvoid用于标识方法返回值的类型, 表示该方法没有返回值
function hello(): void{ alert('Hello World'); }
nullnull表示对象值缺失
undefinedundefined用于初始化变量为一个未定义的值
nevernevernever是其他类型(包括 null 和 undefined) 的子类型, 代表从不会出现的值.

enum(枚举): 定义一个可枚举的对象

const obj = {
    'RUN':'run',
    'EAT':'eat'
}
enum Obj {
    'RUN',// 默认 RUN = 0
    'EAT', // 默认 EAT = 1
    'ABC' = 'abc', // 使用 '=' 复制
}
// 使用
const eat = Obj.EAT;

const getValue = () => {
  return 0
}

enum List {
  A = getValue(),
  B = 2,  // 此处必须要初始化值,不然编译不通过
  C
}
console.log(List.A) // 0
console.log(List.B) // 2
console.log(List.C) // 3

type

type Action = 'eat'  | 'run';
// 声明 a 变量类型为 Action, 可选值为:'eat', 'run'
const a:Action = 'eat';

interface

interface data {
    name: string,
    age: number
};
const b: data = {
    name: 'yhh',
    age: 10
}
// 定义axios.post 返回值类型
axios.post<data>().then();

联合类型: |

交叉类型: &

any 类型

任意值是 typescript 针对编程时类型不明确的变量使用的一种数据类型. 常用于以下三种情况

// 变量的值会动态改变时, 比如来着用户的输入, 任意值类型可以让这些变量跳过编译阶段的类型检查
let x: any = 1;
x = 'yhh';
x = false;

// 改写现有代码时, 任意值允许在编译时可选择地包含或移除类型检查
let x: any = 4;
x.isEmpty(); // 正确, isEmpty方法在运行时可能存在, 但这里并不会检查
x.toFixed(); // 正确

// 定义存储各种类型数据的数组时
ler arrayList: any[] = [1,false,'fine'];
arratList[1] = 100;

null 和 undefined

null

nullJavaScript 中 表示’什么都没有’.

null 是一个只有一个值的特殊类型, 表示一个空对象引用.

typeof 检测 null 返回是 object.

undefined

undefinedjavascript 中是一个没有设置值的变量.

typeof 一个没有值的变量会返回 undefined.

NullUndefined 是其他任何类型(包括void)的子类型. 可以赋值给其他类型, 比如数字类型, 赋值后的类型会变成 nullundefined.

typescript 中启用严格的空检验(--strictnullChecks)特性, 就可以使 nullundefined 只能被赋值给 void 或本身对应的类型.

// 启用 --strictnullChecks 
let x : number;
x = 1;
x = undefined; // 错误, 因为 x 只能是数字类型
x = null; // 错误

// 如果一个类型可能出现 null 或 undefined, 可以使用 | 类支持多中类型
// 启用 --strictnullChecks 
let x : number | null | undefined;
x = 1;
x = undefined; // 正确
x = null; // 正确

never类型

never 是其他类型(包括nullundefined)的子类型, 代表从不会出现的值. 这意味着声明never类型的变量只能被never类型所赋值, 在函数中通常表现为抛出异常或无法执行到终止点.

let x : never;
let y : number;

x = 123; // 错误, 数字类型不可转为 never 类型

// 正确, never 类型可以赋值给 never类型
x = (()=>{ throw new Error('exception') })();
// 正确, never 类型可以赋值给 数字类型
y = (()=>{ throw new Error('exception') })();

// 返回值为 never 的函数可以是抛出异常的情况
function error(message:string): never{
    throw new Error(message);
}
// 返回值为 never 的函数可以是无法被执行到终止点的情况
function loop(): never{
    while (true){}
}

面试题及实战

1. 你觉得使用ts的好处是什么?

1.1 TypeScript是JavaScript的加强版,它给JavaScript添加了可选的静态类型和基于类的面向对象编程,它拓展了JavaScript的语法。所以ts的功能比js只多不少.
1.2 Typescript 是纯面向对象的编程语言,包含类和接口的概念.
1.3 TS 在开发时就能给出编译错误, 而 JS 错误则需要在运行时才能暴露。
1.4 作为强类型语言,你可以明确知道数据的类型。代码可读性极强,几乎每个人都能理解。
1.5 ts中有很多很方便的特性, 比如可选链.

2. type 和 interface的异同

重点:用interface描述数据结构,用type描述类型

2.1 都可以描述一个对象或者函数

interface User {
  name: string
  age: number
}

interface SetUser {
  (name: string, age: number): void;
}

type User = {
  name: string
  age: number
};

type SetUser = (name: string, age: number)=> void;

2.2 都允许拓展(extends)
interface 和 type 都可以拓展,并且两者并不是相互独立的,也就是说 interface 可以 extends type, type 也可以 extends interface 。 虽然效果差不多,但是两者语法不同。

// interface extends interface
interface Name { 
  name: string; 
}
interface User extends Name { 
  age: number; 
}

// type extends type
type Name = { 
  name: string; 
}
type User = Name & { age: number  };

// interface extends type
type Name = { 
  name: string; 
}
interface User extends Name { 
  age: number; 
}

// type extends interface
interface Name { 
  name: string; 
}
type User = Name & { 
  age: number; 
}

2.3 只有type可以做的

type 可以声明基本类型别名,联合类型,元组等类型

// 基本类型别名
type Name = string

// 联合类型
interface Dog {
    wong();
}
interface Cat {
    miao();
}

type Pet = Dog | Cat

// 具体定义数组每个位置的类型
type PetList = [Dog, Pet]

// 当你想获取一个变量的类型时,使用 typeof
let div = document.createElement('div');
type B = typeof div

3. 如何基于一个已有类型, 扩展出一个大部分内容相似, 但是有部分区别的类型?

首先可以通过Pick和Omit

interface Test {
    name: string;
    sex: number;
    height: string;
}

type Sex = Pick<Test, 'sex'>;

const a: Sex = { sex: 1 };

type WithoutSex = Omit<Test, 'sex'>;

const b: WithoutSex = { name: '1111', height: 'sss' };

比如Partial, Required.

再者可以通过泛型.

4. 什么是泛型, 泛型的具体使用?

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

可以把泛型理解为代表类型的参数

interface Test<T = any> {
    userId: T;
}

type TestA = Test<string>;
type TestB = Test<number>;

const a: TestA = {
    userId: '111',
};

const b: TestB = {
    userId: 2222,
};

5. 写一个计算时间的装饰器

export function before(beforeFn: any) {
    return function(target: any, name: any, descriptor: any) {
        const oldValue = descriptor.value;

        descriptor.value = function() {
            beforeFn.apply(this, arguments);
            return oldValue.apply(this, arguments);
        };

        return descriptor;
    };
}

export function after(afterFn: any) {
    return function(target: any, name: any, descriptor: any) {
        const oldValue = descriptor.value;

        descriptor.value = function() {
            const ret = oldValue.apply(this, arguments);
            afterFn.apply(this, arguments);
            return ret;
        };

        return descriptor;
    };
}
// 计算时间
export function measure(target: any, name: any, descriptor: any) {
    const oldValue = descriptor.value;

    descriptor.value = async function() {
        const start = Date.now();
        const ret = await oldValue.apply(this, arguments);
        console.log(`${name}执行耗时 ${Date.now() - start}ms`);
        return ret;
    };

    return descriptor;
}

6. 写一个缓存的装饰器

const cacheMap = new Map();

export function EnableCache(target: any, name: string, descriptor: PropertyDescriptor) {
    const val = descriptor.value;
    descriptor.value = async function(...args: any) {
        const cacheKey = name + JSON.stringify(args);
        if (!cacheMap.get(cacheKey)) {
            const cacheValue = Promise.resolve(val.apply(this, args)).catch((_) => cacheMap.set(cacheKey, null));
            cacheMap.set(cacheKey, cacheValue);
        }
        return cacheMap.get(cacheKey);
    };
    return descriptor;
}

7. 实现一个路由跳转 通过ts约束参数的routeHelper

import { Dictionary } from 'vue-router/types/router';
import Router, { RoutePath } from '../router';

export type BaseRouteType = Dictionary<string>;

export interface IndexParam extends BaseRouteType {
    name: string;
}

export interface AboutPageParam extends BaseRouteType {
    testName: string;
}

export interface UserPageParam extends BaseRouteType {
    userId: string;
}

export interface ParamsMap {
    [RoutePath.Index]: IndexParam;
    [RoutePath.About]: AboutPageParam;
    [RoutePath.User]: UserPageParam;
}


export class RouterHelper {
    public static replace<T extends RoutePath>(routePath: T, params: ParamsMap[T]) {
        Router.replace({
            path: routePath,
            query: params,
        });
    }

    public static push<T extends RoutePath>(routePath: T, params: ParamsMap[T]) {
        Router.push({
            path: routePath,
            query: params,
        });
    }
}

8. 实现一个基于ts和事件模式的countdown基础类

import { EventEmitter } from 'eventemitter3';

export interface RemainTimeData {
    /** 天数 */
    days: number;
    /**
     * 小时数
     */
    hours: number;
    /**
     * 分钟数
     */
    minutes: number;
    /**
     * 秒数
     */
    seconds: number;
    /**
     * 毫秒数
     */
    count: number;
}

export type CountdownCallback = (remainTimeData: RemainTimeData, remainTime: number) => void;

enum CountdownStatus {
    running,
    paused,
    stoped,
}
// 练习
interface User {
    name: string
    age: number
}
function test<T extends User>(params: T): T{
    return params
}
test({name:'1',age:1});

export enum CountdownEventName {
    START = 'start',
    STOP = 'stop',
    RUNNING = 'running',
}
interface CountdownEventMap {
    [CountdownEventName.START]: [];
    [CountdownEventName.STOP]: [];
    [CountdownEventName.RUNNING]: [RemainTimeData, number];
}

export function fillZero(num: number) {
    return `0${num}`.slice(-2);
}

export class Countdown extends EventEmitter<CountdownEventMap> {
    private static COUNT_IN_MILLISECOND: number = 1 * 100;
    private static SECOND_IN_MILLISECOND: number = 10 * Countdown.COUNT_IN_MILLISECOND;
    private static MINUTE_IN_MILLISECOND: number = 60 * Countdown.SECOND_IN_MILLISECOND;
    private static HOUR_IN_MILLISECOND: number = 60 * Countdown.MINUTE_IN_MILLISECOND;
    private static DAY_IN_MILLISECOND: number = 24 * Countdown.HOUR_IN_MILLISECOND;

    private endTime: number;
    private remainTime: number = 0;
    private status: CountdownStatus = CountdownStatus.stoped;
    private step: number;

    constructor(endTime: number, step: number = 1e3) {
        super();

        this.endTime = endTime;
        this.step = step;

        this.start();
    }

    public start() {
        this.emit(CountdownEventName.START);

        this.status = CountdownStatus.running;
        this.countdown();
    }

    public stop() {
        this.emit(CountdownEventName.STOP);

        this.status = CountdownStatus.stoped;
    }

    private countdown() {
        if (this.status !== CountdownStatus.running) {
            return;
        }

        this.remainTime = Math.max(this.endTime - Date.now(), 0);

        this.emit(CountdownEventName.RUNNING, this.parseRemainTime(this.remainTime), this.remainTime);

        if (this.remainTime > 0) {
            setTimeout(() => this.countdown(), this.step);
        } else {
            this.stop();
        }
    }

    private parseRemainTime(remainTime: number): RemainTimeData {
        let time = remainTime;

        const days = Math.floor(time / Countdown.DAY_IN_MILLISECOND);
        time = time % Countdown.DAY_IN_MILLISECOND;

        const hours = Math.floor(time / Countdown.HOUR_IN_MILLISECOND);
        time = time % Countdown.HOUR_IN_MILLISECOND;

        const minutes = Math.floor(time / Countdown.MINUTE_IN_MILLISECOND);
        time = time % Countdown.MINUTE_IN_MILLISECOND;

        const seconds = Math.floor(time / Countdown.SECOND_IN_MILLISECOND);
        time = time % Countdown.SECOND_IN_MILLISECOND;

        const count = Math.floor(time / Countdown.COUNT_IN_MILLISECOND);

        return {
            days,
            hours,
            minutes,
            seconds,
            count,
        };
    }
}
使用
<template>
  <div class="home" @click="toAboutPage">
    <img alt="Vue logo" src="../assets/logo.png">
    <HelloWorld msg="Welcome to Your Vue.js + TypeScript App"/>
    <div>
        倒计时:{{timeDisplay}}
    </div>
  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import HelloWorld from '../components/HelloWorld.vue';
import { RouterHelper } from '../lib/routerHelper';
import { Countdown, CountdownEventName, fillZero } from '../lib/countdown';
import { RoutePath } from '../router';
import { measure } from '../decorator';

@Component({
  components: {
    HelloWorld,
  },
})
export default class Home extends Vue {
    public timeDisplay: string = '';

    @measure
    public toAboutPage() {
        RouterHelper.push(RoutePath.About, {  testName: '1111' });
    }

    public created() {
        const countdown = new Countdown(Date.now() + 60 * 60 * 1000, 10);
        countdown.on(CountdownEventName.RUNNING, (remainTimeData) => {
            const { hours, minutes, seconds, count} = remainTimeData;
            this.timeDisplay = [hours, minutes, seconds, count].map(fillZero).join(':');
        });
    }
}
</script>

9. npm install一个模块,都发生了什么

npm install命令输入 > 检查node_modules目录下是否存在指定的依赖 > 如果已经存在则不必重新安装 > 若不存在,继续下面的步骤 > 向 registry(本地电脑的.npmrc文件里有对应的配置地址)查询模块压缩包的网址 > 下载压缩包,存放到根目录里的.npm目录里 > 解压压缩包到当前项目的node_modules目录中。

10. eventemitter3

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值