某大厂给前端面试者出了一套 TypeScript 笔试题,要求面试者在线实时答题。这种面试题考察的是应聘者的 TS 硬实力,先把题目和要求给出来,你试试能做出来几个。
所有题目均为 NodeJS 环境下,TypeScript 编程题。NodeJS 版本建议 v14 及以上。代码题以本地可以运行通过为准,Node 版本不够则无法运行高版本语法。请提前配置好开发环境。
npm i ts-node -g
NodeJS 环境运行 TS 文件推荐使用 ts-node,建议提前安装。
运行 ts-node
运行第一题命令如下:
ts-node src/1.reverseWord.ts
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
}
}
/**
* @file objToArray
*
* 将对象按照要求转为数组
* 注意console示例运行结果
*/
type Obj = Record;
interface FormatItem {
key: string;
op: string;
value: string;
}
function objToArray(obj: Record): FormatItem[] {
// 补全此处代码
throw new Error("功能待实现");
}
console.log(
objToArray({
key1: {
op1: "value1",
},
key2: {
op2: "value2",
},
})
);
// result示例
// [
// {key: 'key1', op: 'op1', value: 'value1'},
// {key: 'key2', op: 'op2', value: 'value2'}
// ]
export default {};
/**
* @file 反转句子
*
* 同时满足以下条件:1、去除首尾空格,2、单词间隔中多个空格变成一个;
* 注意console示例运行结果
*/
function reverseWord(str: string) {
// 补全此处代码
throw new Error('功能待实现');
}
console.log(reverseWord('the sky is blue')); // blue is sky the
// 去除首尾空格
console.log(reverseWord(" hello world ")); // world hello
// 单词间隔中多个空格变成一个
console.log(reverseWord("a good example")); // example good a
export default {}
/**
* @file 找出字符串中第一个只出现一次的字符
*/
function firstSingleChar(str: string) {
// 补全此处代码
throw new Error('功能待实现');
}
// a 和 b 都出现了两次,只有 c 出现了一次,返回 c
console.log(firstSingleChar('abcba')) // c
// b c d 都出现了一次,返回第一个
console.log(firstSingleChar('aabcdee')) // b
// a 和 b 都出现了多次,没有只出现一次的元素,返回 undefined
console.log(firstSingleChar('aaaabbbb')) // undefined
export default {}
/**
* @file 合并两个有序数组
*/
function merge(arr: number[], arr2: number[]): number[] {
// 补全此处代码
throw new Error('功能待实现');
}
// 参数数组从小到大排列
console.log(merge([1, 2, 3], [2, 5, 6])) // [ 1, 2, 2, 3, 5, 6 ]
export default {}
/**
* @file 实现数组 map 方法
*/
function myMap(arr: T[], callbackFn: (v: T) => R): R[] {
// 补全此处代码,可以使用除数组 map 以外的其他任何函数
throw new Error('功能待实现');
}
// 测试
console.log(myMap([1, 2, 3], v => v * 2)) // [2, 4, 6]
export default {};
/**
* @file 二叉树所有路径
*/
type Tree = {
value: number;
left?: Tree;
right?: Tree;
}
const tree: Tree = {
value: 1,
left: {
value: 2,
right: { value: 5 }
},
right: { value: 3 }
};
function treePath(root: Tree): string[] {
// 补全此处代码
throw new Error('功能待实现');
}
console.log(treePath(tree)) // [ '1->2->5', '1->3' ]
export default {}
/**
* @file 树结构映射
* 数组 map 保持数组长度相同,将对应位置元素进行映射。
* 与之类似,在二叉树 Tree 上的映射我们称为 mapTree,该函数返回一个结构相同的新树,对应位置 value 字段经过 fn 映射。
*/
type Tree = {
value: number;
left?: Tree;
right?: Tree;
}
function mapTree(tree: Tree, fn: (v: number) => number): Tree {
// 补全此处代码
throw new Error('功能待实现');
}
// 测试
const tree: Tree = {
value: 1,
left: { value: 2 },
right: { value: 3 }
};
console.log(mapTree(tree, v => v * 2)) // { value: 2, left: { value: 4 }, right: { value: 6 } }
export default {};
/**
* @file 计算数组笛卡尔积
*/
// 示例
product([1, 2], [3, 4]) // [[1, 3], [1, 4], [2, 3], [2, 4]]
function product(xList: number[], yList: number[]): [number, number][] {
// 补全内部实现
throw new Error('功能待实现');
}
export default {}
/**
* @file 返回一个 Promise,并在 ms 毫秒后 Promise 变为完成状态
*/
export function sleep(ms: number): Promise {
// 补全此处代码
throw new Error('功能待实现');
}
async function main(){
console.log('a')
await sleep(1000);
console.log('b');
await sleep(1000);
console.log('c');
}
main()
export default {}
/**
* @file 实现 PromiseAll 方法
*/
import { sleep } from "./8.sleep";
async function myAll(values: T): Promise<{ [P in keyof T]: Awaited }> {
// 补全此处代码,使用 Promise.all 以外的语法完成
throw new Error('功能待实现');
}
// 一秒钟后返回结果 value
async function request(value: string) {
await sleep(1000);
return value;
}
async function main() {
console.log('start');
const res = await myAll([
request('a'),
request('b'),
request('c'),
])
console.log(res); // 预期输出 start 一秒后输出 ['a', 'b', 'c']
}
main()
export default {}
/**
* @file 假设加法是一个异步过程,如何计算多个数组之和?
*/
function sleep(ms: number) {
return new Promise(r => {
setTimeout(() => {
r(undefined)
}, ms);
})
}
async function asyncAdd(a: number, b: number) {
await sleep(1000);
return a + b;
}
function sum(arr: number[]): Promise {
// 补全这里代码,涉及 arr 中两数求和只能使用 asyncAdd,禁止使用加号
throw new Error('功能待实现');
}
console.time('a')
sum([1, 2, 3, 4, 5, 6, 7, 8])
.then(v => {
console.log(v) // 36
console.timeEnd('a') // a: <耗时>
})
export default {}
/**
* @file objToArray
*
* 将对象按照要求转为数组
* 注意console示例运行结果
*/
type Obj = Record;
interface FormatItem {
key: string;
op: string;
value: string;
}
function objToArray(obj: Record): FormatItem[] {
// 参考答案
return Object.keys(obj).reduce((value: Array, key: string) => {
var op: string = Object.keys(obj[key])[0];
value.push({ key: key, op: op, value: obj[key][op] });
return value;
}, []);
}
console.log(
objToArray({
key1: {
op1: "value1",
},
key2: {
op2: "value2",
},
})
);
// result示例
// [
// {key: 'key1', op: 'op1', value: 'value1'},
// {key: 'key2', op: 'op2', value: 'value2'}
// ]
export default {};
/**
* @file 反转句子
*
* 同时满足以下条件:1、去除首尾空格,2、单词间隔中多个空格变成一个;
* 注意console示例运行结果
*/
function reverseWord(str: string):string {
// 参考答案
return (str.match(/\S+/g)).reverse().join(" ");
}
console.log(reverseWord('the sky is blue')); // blue is sky the
// 去除首尾空格
console.log(reverseWord(" hello world ")); // world hello
// 单词间隔中多个空格变成一个
console.log(reverseWord("a good example")); // example good a
export default {}
/**
* @file 找出字符串中第一个只出现一次的字符
*/
function firstSingleChar(str: string) {
// 参考答案
return str.split("").filter((item: string, index: number, arr: string[]) => {
arr.splice(index, 1);
return !arr.includes(item);
})[0];
}
// a 和 b 都出现了两次,只有 c 出现了一次,返回 c
console.log(firstSingleChar("abcba")); // c
// b c d 都出现了一次,返回第一个
console.log(firstSingleChar("aabcdee")); // b
// a 和 b 都出现了多次,没有只出现一次的元素,返回 undefined
console.log(firstSingleChar("aaaabbbb")); // undefined
console.log(firstSingleChar("dabvb"));
export default {};
/**
* @file 合并两个有序数组
*/
function merge(arr: number[], arr2: number[]): number[] {
// 参考答案
return arr.concat(arr2).sort((a: number, b: number) => a - b);
}
// 参数数组从小到大排列
console.log(merge([1, 2, 3], [2, 5, 6])); // [ 1, 2, 2, 3, 5, 6 ]
export default {};
/**
* @file 实现数组 map 方法
*/
function myMap(arr: T[], callbackFn: (v: T) => R): R[] {
// 参考答案
var arr1: R[] = [];
for (var i = 0; i < arr.length; i++) {
if (i in arr) arr1[i] = callbackFn(arr[i]);
}
return arr1;
}
// 测试
console.log(myMap([1, 2, 3], (v) => v * 2)); // [2, 4, 6]
export default {};
/**
* @file 二叉树所有路径
*/
type Tree = {
value: number;
left?: Tree;
right?: Tree;
};
const tree: Tree = {
value: 1,
left: {
value: 2,
right: { value: 5 },
},
right: { value: 3 },
};
function treePath(root: Tree): string[] {
// 补全此处代码
// throw new Error('功能待实现');
const answer: [] = [];
let tmp: [][] = [];
const travel = (r: Tree) => {
if (r == null) {
return;
}
//@ts-ignore
tmp.push(r.value);
if (r.left == null && r.right == null) {
//@ts-ignore
answer.push(tmp);
tmp = [tmp[0]];
return;
}
if (r.left) travel(r.left);
if (r.right) travel(r.right);
};
travel(root);
//@ts-ignore
return answer.map((t) => t.join("->"));
}
console.log(treePath(tree)); // [ '1->2->5', '1->3' ]
export default {};
/**
* @file 树结构映射
* 数组 map 保持数组长度相同,将对应位置元素进行映射。
* 与之类似,在二叉树 Tree 上的映射我们称为 mapTree,该函数返回一个结构相同的新树,对应位置 value 字段经过 fn 映射。
*/
type Tree = {
value: number;
left?: Tree;
right?: Tree;
};
function mapTree(tree: Tree, fn: (v: number) => number): Tree {
// 参考答案
if (tree == null) {
return tree;
}
tree.value = fn(tree.value);
if (tree.left) mapTree(tree.left, fn);
if (tree.right) mapTree(tree.right, fn);
return tree;
}
// 测试
const tree: Tree = {
value: 1,
left: { value: 2 },
right: { value: 3 },
};
console.log(mapTree(tree, (v) => v * 2)); // { value: 2, left: { value: 4 }, right: { value: 6 } }
export default {};
/**
* @file 计算数组笛卡尔积
*/
// 示例
console.log(product([1, 2], [3, 4])); // [[1, 3], [1, 4], [2, 3], [2, 4]]
function product(xList: number[], yList: number[]): [number, number][] {
// 参考答案
return xList.reduce((v, t) => {
return v.concat(yList.map((item) => [t, item]));
}, [] as [number, number][]);
}
export default {};
/**
* @file 返回一个 Promise,并在 ms 毫秒后 Promise 变为完成状态
*/
export function sleep(ms: number): Promise {
// 参考答案
return new Promise(
(
resolve: (value: undefined) => void,
reject: (value: undefined) => void
) => {
setTimeout(() => {
resolve(undefined);
}, ms);
}
);
}
async function main() {
console.log("a");
await sleep(1000);
console.log("b");
await sleep(1000);
console.log("c");
}
main();
export default {};
/**
* @file 实现 PromiseAll 方法
*/
import { sleep } from "./8.sleep";
async function myAll(
values: T
): Promise<{ [P in keyof T]: Awaited }> {
// 补全此处代码,使用 Promise.all 以外的语法完成
// throw new Error('功能待实现');
var arr = [];
for (var i = 0; i < values.length; i++) {
arr.push(await values[i]);
}
return arr as { [P in keyof T]: Awaited };
}
// 一秒钟后返回结果 value
async function request(value: string) {
await sleep(1000);
return value;
}
async function main() {
console.log("start");
const res = await myAll([request("a"), request("b"), request("c")]);
console.log(res); // 预期输出 start 一秒后输出 ['a', 'b', 'c']
}
main();
export default {};
/**
* @file 假设加法是一个异步过程,如何计算多个数组之和?
*/
function sleep(ms: number) {
return new Promise((r) => {
setTimeout(() => {
r(undefined);
}, ms);
});
}
async function asyncAdd(a: number, b: number) {
await sleep(1000);
return a + b;
}
async function sum(arr: number[]): Promise {
// 参考答案
var s: number = arr[0];
for (var i = 1; i < arr.length; i++) {
s = await asyncAdd(s, arr[i]);
}
return s;
}
console.time("a");
sum([1, 2, 3, 4, 5, 6, 7, 8]).then((v) => {
console.log(v); // 36
console.timeEnd("a"); // a: <耗时>
});
export default {};