TS面试题实例代码分析

发布时间:2023-01-16 13:45:28 作者:iii
来源:亿速云 阅读:176

TS面试题实例代码分析

TypeScript(简称TS)是一种由微软开发的开源编程语言,它是JavaScript的超集,添加了可选的静态类型和基于类的面向对象编程。随着TypeScript在前端开发中的广泛应用,越来越多的公司在面试中加入了TypeScript相关的题目。本文将通过几个典型的TS面试题实例代码,深入分析其背后的原理和考察点,帮助读者更好地理解和掌握TypeScript。

1. 类型推断与类型注解

题目1:类型推断

let x = 3;
x = "hello"; // 这里会报错吗?

分析

TypeScript具有强大的类型推断能力。在上述代码中,变量x被初始化为数字3,TypeScript会自动推断出x的类型为number。因此,当尝试将字符串"hello"赋值给x时,TypeScript会报错,提示类型不匹配。

考察点

题目2:类型注解

let y: number;
y = "hello"; // 这里会报错吗?

分析

在这个例子中,变量y被显式地注解为number类型。因此,当尝试将字符串"hello"赋值给y时,TypeScript会报错,提示类型不匹配。

考察点

2. 接口与类型别名

题目3:接口

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

const person: Person = {
  name: "Alice",
  age: 25,
};

person.age = "30"; // 这里会报错吗?

分析

在这个例子中,我们定义了一个Person接口,它要求name属性为string类型,age属性为number类型。当尝试将字符串"30"赋值给person.age时,TypeScript会报错,因为age属性的类型应为number

考察点

题目4:类型别名

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

const person: Person = {
  name: "Bob",
  age: 30,
};

person.age = "40"; // 这里会报错吗?

分析

类型别名Person与接口Person的功能类似,都用于定义对象的形状。当尝试将字符串"40"赋值给person.age时,TypeScript会报错,因为age属性的类型应为number

考察点

3. 泛型

题目5:泛型函数

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

const output1 = identity<string>("hello");
const output2 = identity<number>(42);
const output3 = identity("world"); // 这里会报错吗?

分析

在这个例子中,identity函数是一个泛型函数,它接受一个类型参数T,并返回相同类型的值。output1output2分别显式地指定了类型参数为stringnumberoutput3没有显式指定类型参数,但TypeScript能够根据传入的参数"world"推断出类型参数为string,因此不会报错。

考察点

题目6:泛型接口

interface GenericIdentityFn<T> {
  (arg: T): T;
}

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

const myIdentity: GenericIdentityFn<number> = identity;

const output = myIdentity("hello"); // 这里会报错吗?

分析

在这个例子中,我们定义了一个泛型接口GenericIdentityFn,它接受一个类型参数T,并要求实现该接口的函数接受一个类型为T的参数并返回类型为T的值。myIdentity变量被注解为GenericIdentityFn<number>类型,因此它只能接受number类型的参数。当尝试将字符串"hello"传递给myIdentity时,TypeScript会报错。

考察点

4. 类与继承

题目7:类与继承

class Animal {
  name: string;

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

  move(distance: number = 0) {
    console.log(`${this.name} moved ${distance}m.`);
  }
}

class Dog extends Animal {
  bark() {
    console.log("Woof! Woof!");
  }
}

const dog = new Dog("Rex");
dog.move(10);
dog.bark();

dog.move("20"); // 这里会报错吗?

分析

在这个例子中,Dog类继承自Animal类,并添加了一个bark方法。dog.move(10)调用Animal类的move方法,输出Rex moved 10m.dog.bark()调用Dog类的bark方法,输出Woof! Woof!。当尝试将字符串"20"传递给dog.move时,TypeScript会报错,因为move方法的参数应为number类型。

考察点

5. 高级类型

题目8:联合类型

function padLeft(value: string, padding: string | number) {
  if (typeof padding === "number") {
    return Array(padding + 1).join(" ") + value;
  }
  if (typeof padding === "string") {
    return padding + value;
  }
  throw new Error(`Expected string or number, got '${padding}'.`);
}

const padded1 = padLeft("Hello", 4);
const padded2 = padLeft("Hello", "   ");
const padded3 = padLeft("Hello", true); // 这里会报错吗?

分析

在这个例子中,padLeft函数的padding参数是一个联合类型,可以是stringnumberpadded1padded2分别传递了numberstring类型的参数,因此不会报错。当尝试将boolean类型的true传递给padding时,TypeScript会报错,因为padding参数的类型应为stringnumber

考察点

题目9:交叉类型

interface Person {
  name: string;
}

interface Employee {
  employeeId: number;
}

type EmployeePerson = Person & Employee;

const employeePerson: EmployeePerson = {
  name: "Alice",
  employeeId: 123,
};

employeePerson.name = "Bob";
employeePerson.employeeId = "456"; // 这里会报错吗?

分析

在这个例子中,EmployeePerson类型是PersonEmployee接口的交叉类型,要求对象同时具有nameemployeeId属性。employeePerson对象符合EmployeePerson类型的定义。当尝试将字符串"456"赋值给employeePerson.employeeId时,TypeScript会报错,因为employeeId属性的类型应为number

考察点

6. 类型守卫与类型断言

题目10:类型守卫

function isString(value: any): value is string {
  return typeof value === "string";
}

function printValue(value: string | number) {
  if (isString(value)) {
    console.log(`String value: ${value}`);
  } else {
    console.log(`Number value: ${value}`);
  }
}

printValue("hello");
printValue(42);
printValue(true); // 这里会报错吗?

分析

在这个例子中,isString函数是一个类型守卫函数,用于判断value是否为string类型。printValue函数接受一个stringnumber类型的参数,并根据类型守卫的结果输出不同的信息。当尝试将boolean类型的true传递给printValue时,TypeScript会报错,因为value参数的类型应为stringnumber

考察点

题目11:类型断言

let someValue: any = "this is a string";

let strLength1: number = (<string>someValue).length;
let strLength2: number = (someValue as string).length;

someValue = 42;
let numLength: number = (someValue as string).length; // 这里会报错吗?

分析

在这个例子中,someValue变量被注解为any类型,因此可以赋值任何类型的值。通过类型断言,我们可以将someValue断言为string类型,并访问其length属性。当someValue被赋值为42时,尝试将其断言为string类型并访问length属性时,TypeScript不会在编译时报错,但在运行时会导致错误。

考察点

7. 模块与命名空间

题目12:模块

// math.ts
export function add(x: number, y: number): number {
  return x + y;
}

// app.ts
import { add } from "./math";

const result = add(2, 3);
console.log(result);

const result2 = add("2", "3"); // 这里会报错吗?

分析

在这个例子中,math.ts模块导出了一个add函数,app.ts模块通过import语句引入了add函数。add函数接受两个number类型的参数,并返回它们的和。当尝试将字符串"2""3"传递给add函数时,TypeScript会报错,因为add函数的参数应为number类型。

考察点

题目13:命名空间

namespace MyMath {
  export function add(x: number, y: number): number {
    return x + y;
  }
}

const result = MyMath.add(2, 3);
console.log(result);

const result2 = MyMath.add("2", "3"); // 这里会报错吗?

分析

在这个例子中,MyMath命名空间定义了一个add函数,用于计算两个number类型参数的和。当尝试将字符串"2""3"传递给MyMath.add函数时,TypeScript会报错,因为add函数的参数应为number类型。

考察点

8. 装饰器

题目14:装饰器

function log(target: any, key: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;

  descriptor.value = function (...args: any[]) {
    console.log(`Calling ${key} with`, args);
    const result = originalMethod.apply(this, args);
    console.log(`Called ${key}, returned`, result);
    return result;
  };

  return descriptor;
}

class Calculator {
  @log
  add(x: number, y: number): number {
    return x + y;
  }
}

const calculator = new Calculator();
const result = calculator.add(2, 3);
console.log(result);

const result2 = calculator.add("2", "3"); // 这里会报错吗?

分析

在这个例子中,log装饰器用于记录Calculator类中add方法的调用和返回结果。add方法接受两个number类型的参数,并返回它们的和。当尝试将字符串"2""3"传递给add方法时,TypeScript会报错,因为add方法的参数应为number类型。

考察点

9. 异步编程

题目15:Promise

function fetchData(): Promise<string> {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("Data fetched");
    }, 1000);
  });
}

async function getData() {
  const data = await fetchData();
  console.log(data);
}

getData();

const data = fetchData();
data.then((result) => console.log(result));

const data2 = fetchData();
data2.then((result) => console.log(result.toFixed())); // 这里会报错吗?

分析

在这个例子中,fetchData函数返回一个Promise,在1秒后解析为字符串"Data fetched"getData函数使用async/await语法等待fetchData的解析结果,并输出datadatadata2变量分别通过then方法处理Promise的解析结果。当尝试调用result.toFixed()时,TypeScript会报错,因为result的类型为string,而toFixednumber类型的方法。

考察点

10. 类型兼容性

题目16:类型兼容性

interface Named {
  name: string;
}

class Person {
  name: string;
  age: number;

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

let p: Named;
p = new Person("Alice", 25); // 这里会报错吗?

分析

在这个例子中,Person类具有nameage属性,而Named接口只要求name属性。由于Person类满足Named接口的要求,因此可以将Person类的实例赋值给Named类型的变量p,不会报错。

考察点

结论

通过以上几个典型的TypeScript面试题实例代码分析,我们可以看到TypeScript在类型推断、类型注解、接口、类型别名、泛型、类与继承、高级类型、类型守卫、类型断言、模块、命名空间、装饰器、异步编程和类型兼容性等方面的强大功能。掌握这些知识点不仅有助于通过面试,还能在实际开发中编写出更加健壮和可维护的代码。希望本文的分析能够帮助读者更好地理解和应用TypeScript。

推荐阅读:
  1. 如何实现ts+vuecli4重构项目
  2. 使用VueCli3怎么构建一个TS项目

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

ts

上一篇:vue插槽能解决的问题是什么

下一篇:node中的模块系统原理是什么

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》