面试题答案
一键面试泛型约束场景
- 接口:
interface KeyValuePair<TKey, TValue> {
key: TKey;
value: TValue;
}
function getValue<TKey, TValue>(pair: KeyValuePair<TKey, TValue>): TValue {
return pair.value;
}
接口定义了一种契约,明确了对象的结构。在泛型约束场景下,通过接口来约束对象结构非常直观,适用于对象形状明确且固定的情况。
- 类型别名:
type KeyValuePairAlias<TKey, TValue> = {
key: TKey;
value: TValue;
};
function getValueAlias<TKey, TValue>(pair: KeyValuePairAlias<TKey, TValue>): TValue {
return pair.value;
}
类型别名在泛型约束场景下同样可以很好地定义对象结构,与接口类似。然而,接口在定义对象类型方面更具传统的面向对象风格,而类型别名则更为灵活,还可以用于其他类型如联合类型、交叉类型等的定义。
泛型扩展场景
- 接口:
interface Animal {
name: string;
}
interface Dog extends Animal {
bark(): void;
}
interface Box<T> {
value: T;
}
interface DogBox extends Box<Dog> {
playWithDog(): void;
}
接口可以通过extends
关键字方便地实现扩展。在泛型扩展场景下,接口能清晰地表达类型之间的继承关系,便于构建复杂的类型层次结构,这符合面向对象编程中对类型继承和层次化设计的理念。
- 类型别名:
type AnimalAlias = {
name: string;
};
type DogAlias = AnimalAlias & {
bark(): void;
};
type BoxAlias<T> = {
value: T;
};
type DogBoxAlias = BoxAlias<DogAlias> & {
playWithDog(): void;
};
类型别名通过交叉类型&
来实现类似的扩展效果。但相比之下,接口的extends
语法更直观地体现继承关系,而类型别名的交叉类型语法在表达复杂继承关系时可能会略显冗长,尤其是当涉及多层扩展时。
总体来说,接口在面向对象风格的类型定义和扩展上表现出色,适合构建清晰的类型层次结构;而类型别名更灵活,不仅能定义对象类型,还能方便地处理联合类型、交叉类型等复杂类型,但在表达继承关系上不如接口直观。