初級面試題
1. 什么是TypeScript?
TypeScript是JavaScript的超集,由Microsoft開發(fā),它添加了可選的靜態(tài)類型和基于類的面向?qū)ο缶幊?。TypeScript旨在解決JavaScript的某些局限性,比如缺乏靜態(tài)類型和基于類的面向?qū)ο缶幊?,同時保持了與JavaScript的兼容性。通過添加這些特性,TypeScript使得代碼更易于維護和擴展,提供了更好的工具和編輯器支持,以及更強大的類型檢查功能。
2. TypeScript中的基本類型有哪些?
TypeScript中的基本類型包括:布爾值(boolean)、數(shù)字(number)、字符串(string)、數(shù)組(Array)、元組(Tuple)、枚舉(Enum)、任意值(any)、空值(void)、Null和Undefined、Never、以及對象(Object,包括普通對象、數(shù)組、函數(shù)等)。此外,TypeScript還支持類型別名(Type Aliases)和映射類型(Mapped Types)等高級類型。
3. TypeScript中如何定義一個變量?
在TypeScript中,可以使用let
、const
或var
關鍵字來聲明變量。let
和const
提供了塊級作用域,而var
提供的是函數(shù)作用域或全局作用域。例如:
let myVariable: string = "Hello, World!";
const myConst: number = 42;
var myVar: boolean = false;
4. 什么是接口(interface)?
接口是TypeScript中的一個核心特性,它用于定義一個對象的結(jié)構(gòu),包括對象應該具有的屬性和方法。接口提供了一種強類型的方式來確保對象實現(xiàn)特定的屬性或方法。通過接口,可以實現(xiàn)代碼的解耦和重用,同時提高代碼的可讀性和可維護性。例如:
interface Person {
name: string;
age?: number; // 可選屬性
greet(): void; // 方法簽名
}
class Student implements Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
greet(): void {
console.log("Hello, my name is " + this.name);
}
}
5. TypeScript中的聯(lián)合類型是什么?
聯(lián)合類型(Union Types)是TypeScript中的一個特性,它允許一個變量有多種類型。聯(lián)合類型通過豎線(|
)分隔每個類型來定義。例如,一個變量可以是字符串或數(shù)字類型:
let id: number | string;
id = 123; // 正確
id = "abc"; // 正確
6. 什么是類型斷言?
類型斷言(Type Assertion)是TypeScript中的一個特性,它允許開發(fā)者顯式地指定一個變量的類型。類型斷言不會進行類型檢查,而是告訴編譯器將變量視為特定的類型。類型斷言有兩種形式:尖括號形式(<類型>值
)和as
形式(值 as 類型
)。例如:
let someValue: any = "Hello World";
let strLength1: number = (<string>someValue).length; // 尖括號形式
let strLength2: number = (someValue as string).length; // as形式
7. 如何定義函數(shù)的返回類型?
在TypeScript中,定義函數(shù)的返回類型很簡單,只需在函數(shù)簽名中的參數(shù)列表后面添加:
和返回類型的名稱即可。例如:
function greet(name: string): string {
return `Hello, ${name}!`;
}
8. 什么是元組(Tuple)?
元組是TypeScript中的一個特性,它允許表示一個已知元素數(shù)量和類型的數(shù)組。元組的每個元素都有固定的類型,并且這些類型不必相同。元組類型通過方括號[]
和逗號,
分隔的類型列表來定義。例如:
let person: [string, number] = ["John Doe", 30];
9. TypeScript中的枚舉(Enum)是什么?
枚舉是TypeScript中的一個特性,它允許為一組相關的值定義一個名稱。枚舉類型使得代碼更加清晰和易于理解。枚舉成員具有一個隱式的數(shù)字值(從0開始遞增),除非顯式地指定了其他值。例如:
enum Color {
Red,
Green,
Blue
}
let favoriteColor: Color = Color.Green;
10. 如何使用TypeScript的模塊?
TypeScript模塊提供了一種將代碼封裝到不同文件中以組織和重用代碼的方法。模塊可以導出和導入到其他文件中,從而更容易跨多個文件和項目重用代碼。TypeScript支持多種模塊規(guī)范,如CommonJS、AMD、ES6模塊等。使用`import`和`export`關鍵字可以導入和導出模塊的功能。例如:
```typescript
// math.ts
export function add(a: number, b: number): number {
return a + b;
}
// main.ts
import { add } from './math';
console.log(add(1, 2)); // 輸出: 3
```
中級面試題
1. 什么是泛型(Generics)?
泛型,從字面上理解,就是一般的、廣泛的的意思。在TypeScript中,泛型(Generics)是指在定義函數(shù)、接口或類的時候,不預先指定具體類型,而是在使用的時候再指定類型的一種特性。泛型中的T就像一個占位符或者說一個變量,在使用的時候可以把定義的類型像參數(shù)一樣傳入,它可以原封不動地輸出。泛型在成員之間提供有意義的約束,這些成員可以是函數(shù)參數(shù)、函數(shù)返回值、類的實例成員、類的方法等。
2. 如何在TypeScript中使用類(Class)?
在TypeScript中,類的定義方式和ES6的class基本相同。以下是一個簡單的例子:
class Person {
name: string = "word";
getName(): void {
console.log(this.name);
}
}
const p1 = new Person();
p1.name = "hello";
p1.getName(); // 輸出 "hello"
在這個例子中,我們定義了一個Person類,該類有一個name屬性和一個getName()方法。我們可以實例化這個類并修改實例的屬性值,然后調(diào)用getName()方法。
3. TypeScript中的接口如何繼承?
在TypeScript中,接口(Interfaces)是支持單繼承的,即一個接口可以繼承自另一個接口。使用extends關鍵字來定義接口的繼承關系。以下是一個單繼承的示例:
interface Animal {
name: string;
eat(): void;
}
interface Dog extends Animal {
bark(): void;
}
let myDog: Dog = {
name: 'Buddy',
eat() { console.log('Dog is eating'); },
bark() { console.log('Woof woof!'); }
};
在這個例子中,Dog接口繼承了Animal接口,并添加了bark方法。任何實現(xiàn)Dog接口的對象都必須實現(xiàn)name、eat和bark這三個屬性和方法。
4. 什么是TypeScript中的類型推斷?
當我們在TypeScript中聲明變量但沒有明確指定其類型時,TypeScript會嘗試根據(jù)變量的值進行類型推斷。這樣可以幫助我們避免手動指定所有類型,使代碼更簡潔,同時也提供了更好的類型安全性。以下是一些關于類型推斷的例子:
let x = 10; // TypeScript 推斷 x 為 number 類型
let y = "hello"; // TypeScript 推斷 y 為 string 類型
let z = true; // TypeScript 推斷 z 為 boolean 類型
5. TypeScript中的交叉類型(Intersection Types)是什么?
TypeScript中的交叉類型(Intersection Types)是通過&符號將多個類型進行合并成一個類型。交叉類型允許我們將多個類型合并為一個類型,從而可以創(chuàng)建一個具有多個類型特性的對象。以下是一個例子:
interface ClassA {
name: string;
age: number;
}
interface ClassB {
name: string;
phone: number;
}
type Class = ClassA & ClassB;
let info: Class = {
name: 'zhangsan',
age: 18,
phone: 15738755555
};
需要注意的是,如果合并的接口類型中具有同名屬性,且類型不同,則合并后類型為never。
6. TypeScript中的映射類型(Mapped Types)是什么?
映射類型(Mapped Types)是TypeScript中的一種高級類型,它允許我們基于一個已存在的類型來創(chuàng)建一個新的類型。映射類型通過遍歷一個對象的所有屬性,并對每個屬性應用一個函數(shù)來生成新的屬性,從而創(chuàng)建一個新的類型。以下是一個簡單的例子:
type Keys = 'a' | 'b' | 'c';
type MappedType = { [P in Keys]: boolean };
在這個例子中,我們定義了一個Keys類型,它是一個字符串字面量類型。然后,我們使用映射類型來創(chuàng)建一個新的類型MappedType,它包含Keys中每個鍵的布爾值屬性。
7. TypeScript中的條件類型(Conditional Types)是什么?
條件類型(Conditional Types)是TypeScript中的一種類型操作,它允許我們根據(jù)一個條件表達式來選擇兩個類型中的一個。條件類型使用三元運算符的語法,以下是一個簡單的例子:
type Message<T> = T extends string ? string : number;
let msg1: Message<string> = "Hello"; // 正確,類型為string
let msg2: Message<number> = 123; // 正確,類型為number
在這個例子中,我們定義了一個條件類型Message,它根據(jù)泛型T的類型來選擇string或number類型。
8. 什么是TypeScript中的命名空間(Namespace)?
命名空間(Namespace)是TypeScript中用于組織代碼的一種方式,它允許我們將相關的代碼(變量、函數(shù)、類等)組織在一起,從而避免命名沖突。以下是一個簡單的例子:
namespace Validation {
export interface StringValidator {
isAcceptable(s: string): boolean;
}
export class LettersOnlyValidator implements StringValidator {
isAcceptable(s: string): boolean {
return /^[A-Za-z]+$/.test(s);
}
}
}
// 使用命名空間中的類和接口
let validator = new Validation.LettersOnlyValidator();
console.log(validator.isAcceptable("Hello")); // 輸出 true
9. TypeScript中的模塊與命名空間有何區(qū)別?
模塊和命名空間都是TypeScript中用于組織代碼的方式,但它們有一些關鍵的區(qū)別:
- 模塊是ES6引入的一個概念,它允許我們將代碼分割成可重用的單元。模塊之間的依賴關系是通過import和export語句來管理的。
- 命名空間是TypeScript特有的一個概念,它提供了一種將代碼組織成層次結(jié)構(gòu)的方式,以避免命名沖突。命名空間通過namespace關鍵字來定義。
模塊和命名空間的主要區(qū)別在于它們的定義方式和使用場景。模塊更適用于大型項目的代碼組織,而命名空間則更適用于小型項目或庫的內(nèi)部代碼組織。
10. 如何在TypeScript中定義和使用裝飾器(Decorators)?
裝飾器(Decorators)是TypeScript中的一個實驗性特性,它允許我們修改類、方法或?qū)傩缘男袨?。裝飾器使用@expression這種語法來應用。以下是一個簡單的例子:
function log(target: any, propertyName: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Calling ${propertyName} with args: ${args}`);
const result = originalMethod.apply(this, args);
console.log(`${propertyName} returned: ${result}`);
return result;
};
return descriptor;
}
class Calculator {
@log
add(a: number, b: number): number {
return a + b;
}
}
const calc = new Calculator();
calc.add(2, 3); // 輸出調(diào)用信息和返回值
在這個例子中,我們定義了一個log裝飾器,它會在調(diào)用被裝飾的方法之前和之后打印日志信息。然后,我們在Calculator類的add方法上應用了這個裝飾器。
請注意,由于裝飾器目前仍然是TypeScript的實驗性特性,因此在使用時可能需要配置TypeScript編譯器以支持該特性。
高級面試題
1. 什么是高級類型(Advanced Types)?
高級類型(Advanced Types)是TypeScript中為了增加語言的靈活性和表達能力而引入的一些特殊類型。這些高級類型允許開發(fā)者定義更具體、更復雜的類型,從而更好地描述數(shù)據(jù)和函數(shù)的行為。TypeScript中的高級類型包括但不限于:
- 字面量類型(Literal Types):字面量不僅可以表示值,還可以表示類型。例如,字符串字面量類型允許指定一個具體的字符串值作為類型。
- 聯(lián)合類型(Union Types):表示一個值可以是幾種類型之一,使用“|”分隔每個類型。
- 交叉類型(Intersection Types):將多個類型合并為一個類型,它包含了所有類型的成員。
- 索引類型:允許定義對象的索引簽名,即對象可以有的屬性和它們的類型。
- 條件類型:根據(jù)條件返回不同的類型,它們使用條件語句的結(jié)構(gòu),但使用類型語法。
- 類型推斷(Type Inference):TypeScript可以自動推斷變量的類型,而無需顯式聲明。
此外,還有類型別名、映射類型、泛型等高級類型特性,這些特性使得TypeScript成為一種強大的靜態(tài)類型系統(tǒng),能夠幫助編寫更健壯、更易于維護的代碼。
2. 如何使用TypeScript進行類型守衛(wèi)(Type Guards)?
類型守衛(wèi)(Type Guards)是TypeScript中的一種特殊表達式,它們可以在運行時檢查一個值是否具有特定類型。類型守衛(wèi)通常與typeof
、instanceof
等操作符結(jié)合使用,或者通過自定義函數(shù)來實現(xiàn)。以下是幾種常見的類型守衛(wèi)使用方式:
typeof
類型守衛(wèi):用于確定變量的類型,但功能有限,只能確定JavaScript能識別的類型(如Boolean、String、Number等)。instanceof
類型守衛(wèi):用于檢查一個值是否是給定構(gòu)造函數(shù)或類的實例。這對于確定實例類型的類型很有用。in
類型守衛(wèi):檢查對象是否具有特定的屬性,并使用該屬性區(qū)分不同的類型。- 自定義類型守衛(wèi):通過自己編寫函數(shù)來創(chuàng)建自定義類型保護,可以檢查的內(nèi)容沒有限制,但需要確保精度以避免錯誤。
3. TypeScript中的反射(Reflection)是什么?
反射(Reflection)是一種在運行時獲取和操作類型及其成員的能力。在TypeScript中,可以使用反射來獲取類的構(gòu)造函數(shù)、屬性、方法等信息,并進行動態(tài)的操作。例如,可以使用typeof
來獲取類的構(gòu)造函數(shù),使用Object.getOwnPropertyNames
來獲取類的屬性和方法。反射是一種強大的工具,但也需要謹慎使用,以避免過度依賴反射導致代碼可讀性和維護性的下降。
4. TypeScript中的類型操縱(Type Manipulation)有哪些技巧?
TypeScript中的類型操縱技巧多種多樣,包括但不限于以下幾種:
- 類型別名(Type Aliases):為類型起一個新名字,以便更簡潔地引用它們。
- 條件類型:根據(jù)條件返回不同的類型,允許在類型級別進行條件判斷。
- 映射類型:基于一個已存在的類型來創(chuàng)建新的類型,新的類型將具有與原類型相同的鍵,但每個鍵對應的類型由提供的函數(shù)來定義。
- 遞歸類型別名:允許定義引用自身的類型,這在處理樹狀結(jié)構(gòu)或嵌套數(shù)據(jù)時特別有用。
- 字符串文字插值類型:允許基于其他類型的動態(tài)創(chuàng)建字符串文本類型,這在處理事件系統(tǒng)或在整個代碼庫中創(chuàng)建一致的命名約定時特別有用。
5. 如何在TypeScript中實現(xiàn)依賴注入(Dependency Injection)?
依賴注入(Dependency Injection)是一種設計模式,用于實現(xiàn)控制反轉(zhuǎn)(Inversion of Control),即類的依賴關系由外部容器來管理,而不是在類內(nèi)部創(chuàng)建依賴對象。在TypeScript中實現(xiàn)依賴注入通常涉及以下幾個步驟:
- 標記可被注入的類:通過某種機制(如裝飾器或注解)標記哪些類可以被容器注入。
- 獲取實例:從容器中獲取類的實例,容器會自動處理依賴注入。
在實際應用中,可以使用第三方庫(如InversifyJS)來實現(xiàn)依賴注入,這些庫提供了更強大和靈活的功能。
6. TypeScript中如何實現(xiàn)混入(Mixins)?
混入(Mixins)是一種將多個類的行為組合到一個類中的技術。在TypeScript中,可以通過使用類型別名、交叉類型、裝飾器等技術來實現(xiàn)混入。以下是一個使用類型別名和交叉類型實現(xiàn)混入的簡單示例:
type Constructor<T> = new (...args: any[]) => T;
function Mixin<TBase extends Constructor<{}>, TMixin extends Constructor<{}>>(base: TBase, mixin: TMixin): Constructor<TBase & InstanceType<TMixin>> {
return class extends base implements InstanceType<TMixin> {
constructor(...args: any[]) {
super(...args);
Object.assign(this, new mixin(...(args as any)));
}
} as any;
}
class A {
foo() {
console.log('foo from A');
}
}
class B {
bar() {
console.log('bar from B');
}
}
const AB = Mixin(A, B);
const ab = new AB();
ab.foo(); // 輸出: foo from A
ab.bar(); // 輸出: bar from B
在這個示例中,Mixin
函數(shù)接受兩個構(gòu)造函數(shù)作為參數(shù),并返回一個新的構(gòu)造函數(shù)。這個新的構(gòu)造函數(shù)創(chuàng)建的實例將同時擁有兩個原始類的行為。
7. 如何使用TypeScript中的類型保護(Type Protection)?
類型保護(Type Protection)與類型守衛(wèi)類似,都是用于在運行時檢查一個值是否具有特定類型。類型保護通常通過自定義函數(shù)來實現(xiàn),這些函數(shù)返回一個布爾值來表示檢查是否成功。與類型守衛(wèi)不同的是,類型保護更側(cè)重于在函數(shù)內(nèi)部對類型進行斷言,而不是在條件語句中。以下是一個使用類型保護的示例:
function isString(value: any): value is string {
return typeof value === 'string';
}
function processValue(value: any) {
if (isString(value)) {
// 在這里,TypeScript知道value是string類型
console.log(value.toUpperCase());
} else {
// 處理其他類型
console.log(value);
}
}
在這個示例中,isString
函數(shù)是一個類型保護函數(shù),它返回一個布爾值來表示給定的值是否是字符串類型。在processValue
函數(shù)中,可以使用這個類型保護函數(shù)來檢查值的類型,并在類型確定的情況下執(zhí)行相應的操作。
8. TypeScript中的模塊解析策略(Module Resolution Strategies)是什么?
TypeScript中的模塊解析策略是指編譯器如何查找和解析模塊的位置。TypeScript支持多種模塊解析策略,包括經(jīng)典解析策略(Classic)和Node.js解析策略(Node)。
- 經(jīng)典解析策略:在這種策略下,TypeScript編譯器會按照一個相對簡單的規(guī)則來解析模塊。它會首先在當前文件所在目錄下查找模塊文件(如
.ts
、.tsx
、.d.ts
等),如果沒有找到,則會在編譯選項中的baseUrl
指定的目錄下查找,或者回退到全局模塊查找。 - Node.js解析策略:這是TypeScript默認的模塊解析策略。它遵循Node.js的模塊解析機制,包括查找
node_modules
目錄、解析package.json
中的main
字段、查找index.js
等默認文件等。此外,TypeScript還支持通過paths
和baseUrl
編譯選項來自定義模塊解析路徑。
9. 什么是TypeScript中的裝飾器(Decorators)?
裝飾器(Decorators)是TypeScript中的一個實驗性特性,它允許你通過聲明式的方式修改類和類成員的行為。裝飾器是一種特殊類型的聲明,它能夠被附加到類聲明、方法、訪問器、屬性或參數(shù)上。裝飾器使用@expression
這種形式,expression
必須求值為一個函數(shù),它會在運行時被調(diào)用,被裝飾的聲明信息作為參數(shù)傳入。
以下是一個使用裝飾器的示例:
function log(target: any, propertyName: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Calling ${propertyName} with args:`, args);
const result = originalMethod.apply(this, args);
console.log(`${propertyName} returned:`, result);
return result;
};
return descriptor;
}
class Calculator {
@log
add(a: number, b: number): number {
return a + b;
}
}
const calc = new Calculator();
calc.add(2, 3); // 輸出調(diào)用和返回信息
在這個示例中,log
是一個裝飾器函數(shù),它會被附加到Calculator
類的add
方法上。當調(diào)用add
方法時,裝飾器函數(shù)會先執(zhí)行,打印出調(diào)用信息和返回值信息。
10. 如何在TypeScript中處理異步編程?
在TypeScript中處理異步編程主要依賴于JavaScript的異步特性,但TypeScript提供了更強大的類型檢查和智能提示功能來增強異步代碼的可讀性和安全性。以下是幾種在TypeScript中處理異步編程的主要方式:
回調(diào)函數(shù)(Callbacks):雖然在現(xiàn)代JavaScript/TypeScript開發(fā)中,回調(diào)函數(shù)已經(jīng)較少作為首選的異步處理方式,但它們?nèi)匀皇抢斫猱惒骄幊痰幕A?;卣{(diào)函數(shù)是一個作為參數(shù)傳遞給另一個函數(shù)的函數(shù),這個被傳遞的函數(shù)將在某個異步操作完成后被調(diào)用。
function fetchData(callback: (data: string) => void) {
setTimeout(() => {
const data = "Fetched Data";
callback(data);
}, 1000);
}
fetchData((data) => {
console.log(data);
});
在TypeScript中,你可以為回調(diào)函數(shù)指定參數(shù)和返回值的類型。
Promises:Promises是處理異步操作更現(xiàn)代、更優(yōu)雅的方式。它們代表了一個異步操作的最終完成(或失?。┘捌浣Y(jié)果值。
function fetchData(): Promise<string> {
return new Promise((resolve, reject) => {
setTimeout(() => {
const success = true; // 模擬成功或失敗的條件
if (success) {
resolve("Fetched Data");
} else {
reject(new Error("Failed to fetch data"));
}
}, 1000);
});
}
fetchData()
.then(data => {
console.log(data);
})
.catch(error => {
console.error(error);
});
在TypeScript中,你可以為Promise指定它解析(resolve)值的類型。
async/await:async/await是基于Promises的語法糖,它使得異步代碼看起來和同步代碼非常相似,從而更容易理解和維護。
async function fetchData(): Promise<string> {
return new Promise((resolve, reject) => {
setTimeout(() => {
const success = true; // 模擬成功或失敗的條件
if (success) {
resolve("Fetched Data");
} else {
reject(new Error("Failed to fetch data"));
}
}, 1000);
});
}
async function getData() {
try {
const data = await fetchData();
console.log(data);
} catch (error) {
console.error(error);
}
}
getData();
使用async關鍵字聲明的函數(shù)總是返回一個Promise。在函數(shù)內(nèi)部,你可以使用await關鍵字等待一個Promise完成,并獲取其結(jié)果。await只能在async函數(shù)內(nèi)部使用。
該文章在 2024/11/8 11:43:20 編輯過