banner
小凯同学 xkfe

小凯同学 xkfe

学而不思则罔,思而不学则殆
twitter
github
email
follow

TypeScript高頻面試題/寫法

接口 (interface) 是什么?#

// 1、 定義對象結構
interface Car {
  brand: string;
  speed: number;
  drive(): void;
}
let myCar: Car = {
  brand: "Toyota",
  speed: 120,
  drive() {
    console.log("Driving at speed: " + this.speed);
  }
};

// 2、指定接口只讀和可選
interface Point {
  readonly x: number; // 只讀
  readonly y: number; // 只讀
  z?: number;  // 可選
}
let p1: Point = { x: 10, y: 20 };

type 和 interface 的區別?#


unknown 和 any 有什麼區別?#

let value: unknown;
value = 42;
// 需要先做類型檢查,才能調用 `toFixed` 方法
if (typeof value === "number") {
  value.toFixed(); // 正確
}

value = "Hello";
// 需要先做類型檢查,才能調用 `toUpperCase` 方法
if (typeof value === "string") {
  value.toUpperCase(); // 正確
}

說一下 never 類型的用法?#

// 拋出異常
function throwError(message: string): never {
  throw new Error(message);
}

// 無限循環
function infiniteLoop(): never {
  while (true) {
    // 無限循環,永遠不會返回
  }
}

// 窮盡檢查
type Shape = 'circle' | 'square';

function getArea(shape: Shape): number {
  switch (shape) {
    case 'circle':
      return Math.PI * 1 ** 2;
    case 'square':
      return 1 * 1;
    default:
      const _exhaustiveCheck: never = shape; // 這裡的 `shape` 永遠不會是其他值
      throw new Error(`Unexpected shape: ${shape}`);
  }
}

never 與 void 的區別?#

function logMessage(message: string): void {
  console.log(message); // 該函數返回 `undefined`
}

function throwError(message: string): never {
  throw new Error(message); // 該函數永遠不會返回
}

說一下聯合類型、交叉類型、類型別名、類型保護#

聯合類型 (Union Types)#

  • 聯合類型 允許一個變量可以是多種類型中的一種。使用 |(豎線)符號來定義。
  • 場景: 用於表示某個值可以是多個不同類型中的一種,比如函數參數可以接受多種類型的數據。
let value: string | number;

function formatInput(input: string | number): string {
  if (typeof input === "string") {
    return input.toUpperCase();
  } else {
    return input.toFixed(2);
  }
}

交叉類型 (Intersection Types)#

  • 交叉類型 將多個類型合併為一個類型,該類型同時擁有所有類型的特性。使用 &(和符號)定義。
  • 場景: 用於組合多個類型的屬性,創建一個擁有所有屬性的對象類型。
interface Person {
  name: string;
}
interface Employee {
  id: number;
}
let employee: Person & Employee = {
  name: "Alice",
  id: 123
};

function printEmployee(employee: Person & Employee) {
  console.log(`Name: ${employee.name}, ID: ${employee.id}`);
}

類型別名 (Type Aliases)#

  • 類型別名 用來為一個類型創建一個新的名稱(別名)。通過 type 關鍵字定義,適用於基本類型、聯合類型、交叉類型等。
  • 場景: 用於簡化複雜類型,提升代碼的可讀性。它可以為對象、函數、聯合類型等複雜類型創建簡單的別名。
type ID = string | number;
let userId: ID = "abc123";

type User = {
  name: string;
  age: number;
};
function greet(user: User) {
  console.log(`Hello, ${user.name}`);
}

類型保護 (Type Guards)#

類型保護 是 ts 中用於判斷某個值的實際類型,並根據該類型進行進一步處理的技術。它在使用聯合類型時非常有用,確保在不同類型的分支中能正確地執行相應的操作。

  1. typeof 類型保護: 用於判斷基本類型(string, number, boolean 等)
function printId(id: string | number) {
  if (typeof id === "string") {
    console.log(id.toUpperCase());
  } else {
    console.log(id.toFixed(2));
  }
}
  1. instanceof 類型保護: 用於判斷對象是否是某個類的實例。
class Dog {
  bark() {
    console.log("Woof!");
  }
}
class Cat {
  meow() {
    console.log("Meow!");
  }
}
function makeSound(animal: Dog | Cat) {
  if (animal instanceof Dog) {
    animal.bark();
  } else {
    animal.meow();
  }
}
  1. is 關鍵字: 可以通過函數返回值 is 來創建自定義類型保護。
function isString(value: any): value is string {
  return typeof value === "string";
}

function printValue(value: string | number) {
  if (isString(value)) {
    console.log(value.toUpperCase());
  } else {
    console.log(value.toFixed(2));
  }
}
  1. in 關鍵字: 用於判斷對象是否具有某個屬性,特別在判斷聯合類型中的對象類型時。
interface Bird {
  fly(): void;
}
interface Fish {
  swim(): void;
}
function move(animal: Bird | Fish) {
  if ("fly" in animal) {
    animal.fly();
  } else {
    animal.swim();
  }
}

typeof 關鍵字的作用是什麼?#

// 1、復用已有變量的類型
let person = {
  name: "Alice",
  age: 25,
};
type PersonType = typeof person; // { name: string; age: number; }

// 2、函數返回類型
function getPerson() {
  return { name: "Alice", age: 25 };
}

type PersonType = typeof getPerson(); // { name: string; age: number; }

// 3、JavaScript 中的類型檢查
function printValue(value: number | string) {
  if (typeof value === "string") {
    console.log("This is a string: ", value);
  } else {
    console.log("This is a number: ", value);
  }
}

keyof 關鍵字的作用是什麼?#

// 1、獲取對象類型的鍵
type Person = {
  name: string;
  age: number;
  location: string;
};

type PersonKeys = keyof Person; // 'name' | 'age' | 'location'

// 2、類型約束(key必須是obj的對象鍵)
function getValue<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}
const person = { name: "Alice", age: 25, location: "NYC" };
const name = getValue(person, "name");
const age = getValue(person, "age");

如何定義一個枚舉?使用場景是?#

enum ResultEnum {
  SUCCESS = 200,
  ERROR = 500,
  NOTLOGGED = 401,
  OVERDUE = 402,
  TIMEOUT = 5000,
}

enum LogLevel {
  Info,
  Warn,
  Error
}

如何聯合枚舉類型的 Key?#

enum Direction {
  Up,
  Down,
  Left,
  Right
}

type DirectionType = keyof typeof Direction; // Up | Down | Left | Right

什麼是泛型 (generics)?#

// 簡單泛型函數
function echo<T>(arg: T): T {
  return arg;
}
echo<number>(42); // 傳入 number 類型

// 泛型接口
interface Box<T> {
  content: T;
}

// 默認泛型類型
function createArray<T = string>(length: number, value: T): T[] {
  return Array(length).fill(value);
}

如何對泛型類型進行約束?#

規定泛型參數必須符合某些條件,比如必須具有某些屬性或方法。

interface Lengthwise {
  length: number;
}
function logLength<T extends Lengthwise>(arg: T): T {
  console.log(arg.length);
  return arg;
}
logLength("Hello"); // 字符串有 length 屬性,合法
// logLength(123); // 這裡會報錯,因為 number 類型沒有 length 屬性

as 作用?什麼時候使用?#

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

// 我們知道 someValue 是 string 類型
let strLength: number = (someValue as string).length;

let colors = ["red", "green", "blue"] as const;
// `colors` 的類型將是 readonly ["red", "green", "blue"]

元組是什麼?和 ts 數組有何區別?#

let tuple: [string, number];
tuple = ["Hello", 42]; // 合法
// tuple = [42, "Hello"]; // 非法,順序錯誤
function getUserInfo(): [string, number] {
  return ["Alice", 25];
}

let entry: [string, number] = ["score", 100];
console.log(entry[0]); // 輸出 "score"

// 可選元素的元組
type FlexibleTuple = [string, number?, boolean?];
let tuple1: FlexibleTuple = ["Hello"];

函數重載是什麼?有何作用?#

// 重載簽名(沒有實現)
function format(input: number): string;
function format(input: Date): string;

// 實現簽名(包含邏輯)
function format(input: any): string {
  if (typeof input === "number") {
    return input.toFixed(2); // 格式化數字
  } else if (input instanceof Date) {
    return input.toISOString(); // 格式化日期
  }
}

// 不同數量、類型參數
function greet(name: string): string;
function greet(firstName: string, lastName: string): string;

function greet(firstName: string, lastName?: string): string {
  if (lastName) {
    return `Hello, ${firstName} ${lastName}!`;
  } else {
    return `Hello, ${firstName}!`;
  }
}

如何實現條件類型?#

條件類型通常以 T extends U ? X : Y 這樣的形式書寫,其中 T extends U 表示條件判斷,如果條件為真則返回類型 X,否則返回類型 Y。這類似於 JavaScript 中的三元操作符 condition ? trueValue : falseValue。

T extends U ? X : Y
// • T extends U:表示條件,檢查類型 T 是否可以賦值給類型 U。
// • X:當 T 滿足條件(即 T extends U 為 true)時返回的類型。
// • Y:當 T 不滿足條件(即 T extends U 為 false)時返回的類型。

// 示例:
type IsString<T> = T extends string ? "Yes" : "No";

type A = IsString<string>;  // "Yes"
type B = IsString<number>;  // "No"

有什麼常用的內置工具類型?#

Partial<T>#

將類型 T 的所有屬性變為可選屬性。

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

type PartialPerson = Partial<Person>;
// PartialPerson 等價於 { name?: string; age?: number; }

Required<T>#

將類型 T 的所有屬性變為必選屬性。

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

type RequiredPerson = Required<Person>;
// RequiredPerson 等價於 { name: string; age: number; }

Readonly<T>#

將類型 T 的所有屬性變為只讀屬性,不允許修改。

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

type ReadonlyPerson = Readonly<Person>;
// ReadonlyPerson 等價於 { readonly name: string; readonly age: number; }

Pick<T, K>#

從類型 T 中選取部分屬性組成新類型。K 是需要選取的屬性的聯合類型。

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

type PersonNameAndAge = Pick<Person, "name" | "age">;
// PersonNameAndAge 等價於 { name: string; age: number; }

Omit<T, K>#

從類型 T 中排除 K 指定的屬性,構造新類型。

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

type PersonWithoutAddress = Omit<Person, "address">;
// PersonWithoutAddress 等價於 { name: string; age: number; }

Record<K,T>#

構造一個類型,鍵 K 可以是字符串、數字等,值 T 是所有屬性的類型。

type Page = "home" | "about" | "contact";
type PageInfo = { title: string };

const pages: Record<Page, PageInfo> = {
  home: { title: "Home Page" },
  about: { title: "About Us" },
  contact: { title: "Contact Us" },
};

Exclude<T,U>#

從聯合類型 T 中排除所有屬於類型 U 的子類型。

type A = "a" | "b" | "c";
type B = "a";

type Result = Exclude<A, B>; // "b" | "c"

Extract<T,U>#

從聯合類型 T 中提取所有可以賦值給類型 U 的子類型。

type A = "a" | "b" | "c";
type B = "a" | "b";

type Result = Extract<A, B>; // "a" | "b"

NonNullable<T>#

排除類型 T 中的 null 和 undefined。

type A = string | null | undefined;

type NonNullableA = NonNullable<A>; // string

ReturnType<T>#

獲取函數類型 T 的返回值類型。

function getUser() {
  return { name: "Alice", age: 25 };
}

type UserType = ReturnType<typeof getUser>;
// UserType 等價於 { name: string; age: number; }

InstanceType<T>#

獲取構造函數類型 T 的實例類型。

class Person {
  name: string = "Alice";
  age: number = 25;
}

type PersonInstance = InstanceType<typeof Person>;
// PersonInstance 等價於 Person

Parameters<T>#

獲取函數類型 T 的參數類型,返回一個元組類型。

function greet(name: string, age: number): string {
  return `Hello ${name}, you are ${age} years old.`;
}

type GreetParams = Parameters<typeof greet>;
// GreetParams 等價於 [string, number]

ConstructorParameters<T>#

獲取構造函數類型 T 的參數類型,返回一個元組類型。

class Person {
  constructor(public name: string, public age: number) {}
}

type PersonConstructorParams = ConstructorParameters<typeof Person>;
// PersonConstructorParams 等價於 [string, number]

ThisType<T>#

用於指定上下文對象 this 的類型,通常與 noImplicitThis 選項配合使用。

type ObjectDescriptor<D, M> = {
  data?: D;
  methods?: M & ThisType<D & M>; // M中的this會被推斷為 D & M 類型
};

let obj: ObjectDescriptor<{ x: number }, { foo(): void }> = {
  data: { x: 10 },
  methods: {
    foo() {
      console.log(this.x); // 推斷為 number
    }
  }
};

Awaited<T>#

獲取一個 Promise 的解析類型。

type P = Promise<string>;

type Result = Awaited<P>; // string

declare,declare global 是什麼?#

.d.ts 和 .ts 文件有何區別?#

如何為組件模板實例引用標註類型?#

<template>
  <MyComponent ref="myComponentRef" message="Hello, World!" />
</template>

<script setup lang="ts">
  import { ref } from 'vue'
  import MyComponent from './MyComponent.vue';
  const myComponentRef = ref<InstanceType<typeof MyComponent> | null>(null)
</script>

typescript.json 配置說明!#

https://typescript.p6p.net/typescript-tutorial/tsconfig.json.html#compileoptions

{
  // 編譯選項
  "compilerOptions": {
    // 指定 ECMAScript 目標版本(如 ES5, ES6, ESNext)。
    "target": "ES6",
    // 指定使用的模塊系統
    "module": "ESNext", 
    // 啟用所有嚴格類型檢查選項的標誌。
    "strict": true,
    // 啟用對 ES 模塊的互操作性支持。
    "esModuleInterop": true,
    // 生成 .map 文件,用於調試。
    "sourceMap": true,
    // 指定 JSX 代碼的編譯方式(如 react, react-jsx, preserve)。
    "jsx": "preserve",
    // 指定使用的庫文件(如 DOM, ES6, ESNext)。
    "lib": ["ES6", "DOM"],
    // 指定使用的庫文件(如 DOM, ES6, ESNext)。
    "skipLibCheck": true,
    // 在沒有類型注解的情況下禁止隱式 any 類型。
    "noImplicitAny": true,
    // 生成聲明文件,開啟後會自動生成聲明文件
    "declaration": true,
    // 指定生成聲明文件存放目錄
    "declarationDir": "./file",
    // 只生成聲明文件,而不會生成js文件
    "emitDeclarationOnly": true,
    // 允許export=導出,由import from 導入
    "esModuleInterop": true,
    // 解析非相對模塊的基地址,默認是當前目錄
    "baseUrl": "./",
    // 路徑映射,相對於baseUrl
    "paths": { 
       "@/*": ["./src/*"]
    },
    // ...
  },
  // 包含的文件或目錄
  "include": [],
  // 排除的文件或目錄
  "exclude": [],
  // 指定編譯的具體文件列表,適用於小型項目。
  "files": [],
  // 用於支持項目間的引用,適用於大型項目。
  "references": []
  // 繼承其他配置
  "extends": ""
}
載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。