1、什么是 Typescript?
TypeScript 是 JavaScript 的一个超集,主要为 JavaScript 提供了类型系统,它由 Microsoft 开发,代码是开源的。
2、interface 和 type 的区别
相同点
interface
interface User {
name: string
age: number
}
interface SetUser {
(name: string, age: number): void;
}
复制代码
type
type User = {
name: string
age: number
};
type SetUser = (name: string, age: number): void;
复制代码
不同点
type 可以而 interface 不行
// 基本类型别名
type Name = string
// 联合类型
interface Dog {
wong();
}
interface Cat {
miao();
}
type Pet = Dog | Cat
复制代码
// 当你想获取一个变量的类型时,使用 typeof
let div = document.createElement('div');
type B = typeof div
复制代码
interface 可以而 type 不行
interface User {
name: string
age: number
}
interface User {
sex: string
}
/*
User 接口为 {
name: string
age: number
sex: string
}
*/
复制代码
相关链接:
typescript 中的 interface 和 type 到底有什么区别?
TypeScript: Interfaces vs Types
3、never 关键词有什么用?
当有一个联合类型:
interface Foo {
type: 'foo'
}
interface Bar {
type: 'bar'
}
type All = Foo | Bar
复制代码
在 switch 当中判断 type,TS 是可以收窄类型的 (discriminated union):
function handleValue(val: All) {
switch (val.type) {
case 'foo':
// 这里 val 被收窄为 Foo
break
case 'bar':
// val 在这里是 Bar
break
default:
// val 在这里是 never
const exhaustiveCheck: never = val
break
}
}
复制代码
注意在 default 里面我们把被收窄为 never 的 val 赋值给一个显式声明为 never 的变量。如果一切逻辑正确,那么这里应该能够编译通过。但是假如后来有一天你的同事改了 All 的类型:
type All = Foo | Bar | Baz
复制代码
然而他忘记了在 handleValue 里面加上针对 Baz 的处理逻辑,这个时候在 default branch 里面 val 会被收窄为 Baz,导致无法赋值给 never,产生一个编译错误。所以通过这个办法,你可以确保 handleValue 总是穷尽 (exhaust) 了所有 All 的可能类型。
参考链接:
尤雨溪:TypeScript中的never类型具体有什么用?
4、is 关键词有什么用?
先看一串代码:
function isString(test: any): boolean{
return typeof test === "string";
}
function example(foo: any){
if(isString(foo)){
console.log("it is a string" + foo);
console.log(foo.length);
console.log(foo.hello());
}
}
example("hello world");
复制代码
上面的的代码不会有编译时错误,但是会有运行时错误。因为 字符串类型上没有 hello
方法。
这时候怎么优化,才能让 TS 在编译时就能检查到错误呢?
is
关键词可以派上用场,参考下面的代码:
function isString(test: any): test is string{
return typeof test === "string";
}
function example(foo: any){
if(isString(foo)){
console.log("it is a string" + foo);
console.log(foo.length);
console.log(foo.hello()); // ts Error: 类型“string”上不存在属性“hello”
}
}
复制代码
我们把 boolean
替换成 test is string
,那么当函数 isString
返回 true 时, TS 会将 test
的类型从 any
收缩到 string
,从而能精确的进行类型判断。
相关链接:
What does the is keyword do in typescript?
5、infer 关键词有什么用?
infer 关键词常在条件类型中和 extends 关键词一同出现,表示将要推断的类型,作为类型变量可以在三元表达式的 True 部分引用。
例如,简单的类型提取:
type Unpacked<T> = T extends (infer U)[] ? U : T;
type T0 = Unpacked<string[]>; // string
type T1 = Unpacked<string>; // string
复制代码
上面提到的 ReturnType
工具类型 也是使用这种方式提取到了函数的返回类型:
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
复制代码
相关链接:
TypeScript 的 Infer 关键词
6、TypeScript 有哪些实用工具类型?
`Partial<T>`
将 T 中所有属性转换为可选属性,返回的类型可以是 T 的任意子集。这在需要支持接受部分属性的场景下非常有用:
interface Todo {
title: string;
description: string;
done: boolean;
}
function updateTodo(todo: Todo, newTodo: Partial<Todo>) {
return { ...todo, ...newTodo };
}
const todo: Todo = {
title: 'First Todo',
description: 'this is the first todo',
done: false
};
updateTodo(todo, { done: true });
复制代码
源码:
type Partial<T> = { [P in keyof T]?: T[P]; };
复制代码
`Pick<T,K>`
通过在 T 中抽取一组属性 K 构建一个新类型:
interface Todo {
title: string;
description: string;
done: boolean;
}
type TodoBase = Pick<Todo, 'title' | 'done'>;
const todo: TodoBase = {
title: 'First Todo',
done: false
};
复制代码
源码:
// K是T的属性集合的子集
type Pick<T, K extends keyof T> = { [p in K]: T[p] };
复制代码
`ReturnType<T>`
返回 function 的返回值类型:
type T0 = ReturnType<() => string>; // string
type T1 = ReturnType<() => Promise<number>>; // Promise<number>
type T2 = ReturnType<(s: string) => void>; // void
复制代码
源码:
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
复制代码
相关链接:
TypeScript Utility Types 源码解析
7、如何处理才能在 TS 中引用 CSS 或者 图片使之不报错?
例如,在下面的 TS 文件中,直接导入 .scss
文件或者 .png
文件,TS 的静态检查会出错:
import S from "./index.scss"; // TS Error: 找不到模块“./index.scss”或其相应的类型声明
import imgLogo from "./logo.png"; // // TS Error: 找不到模块“./logo.png”或其相应的类型声明
复制代码
这时候需要编写 TS 申明文件,可以在项目根目录下添加一个 index.d.ts
文件,其中写入:
declare module '*.png';
declare module '*.module.scss' {
const classes: { [key: string]: string };
export default classes;
}
复制代码
8、tsconfig.json 熟悉吗?有哪些常用属性?
一般的 tsconfg.json
可以如下配置:
{
"compilerOptions": {
"outDir": "./dist/",
"sourceMap": true,
"allowSyntheticDefaultImports": true,
"skipLibCheck": true,
"moduleResolution": "node",
"module": "esnext",
"target": "es5",
"noImplicitAny": false,
"jsx": "react",
"allowJs": true,
"baseUrl": "./",
"paths": {
"@utils/*": [
"src/utils/*"
],
"@containers/*": [
"src/containers/*"
],
"@components/*": [
"src/components/*"
],
}
}
}
复制代码
其中有一些常用的属性,例如:
1、allowJs: true
允许导入 .js
文件
2、paths
设置别名
3、allowSyntheticDefaultImports:true
// 这样不会报错
import React, { Component} from 'react';
// 要不然只能
import * as React from 'react';
复制代码
4、moduleResolution:"node"
//可以直接引用文件夹
import Head from '@components/head';
// 而不需要
import Head from '@components/head/index';
复制代码
9、TS 的装饰器有没有用过?试着写一个用于计算函数执行时间的的装饰器
/**
* 装饰器方法 , 用于计算运行时间
*
* @param target 目标对象
* @param propertyName 属性名
* @param propertyDescriptor 属性描述器
*/
export function runtime(
target: Object,
propertyName: string,
propertyDescriptor: PropertyDescriptor): PropertyDescriptor {
const method = propertyDescriptor.value;
propertyDescriptor.value = function(...args) {
console.time(propertyName);
const result = method.apply(this, args);
console.timeEnd(propertyName);
return result;
};
return propertyDescriptor;
}
复制代码
10、一道 LeetCode 招聘面试题
下面是一道 LeetCode 面试题:
// 假设有一个这样的类型:
interface initInterface {
count: number;
message: string;
asyncMethod(input: Promise<string>): Promise<Action<number>>;
syncMethod(action: Action<string>): Action<number>;
}
// 在经过 Connect 函数之后,返回值类型为
interface Result {
asyncMethod(input: string): Action<number>;
syncMethod(action: string): Action<number>;
}
// 其中 Action<T> 的定义为:
interface Action<T> {
payload?: T
type: string
}
// 现在要求写出Connect的函数类型定义。
复制代码
答案:
type RemoveNonFunctionProps<T> = {
[K in keyof T]: T[K] extends Function ? K : never;
}[keyof T];
type PickFunction<T> = Pick<T, RemoveNonFunctionProps<T>>;
type TransformMethod<T> = T extends (
input: Promise<infer U>
) => Promise<Action<infer S>>
? (input: U) => Action<S>
: T extends (action: Action<infer U>) => Action<infer S>
? (action: U) => Action<S>
: never;
type ConnectAll<T> = {
[K in keyof T]: TransformMethod<T[K]>;
};
type Connect<T> = ConnectAll<PickFunction<T>>;
复制代码
参考链接:
不能不掌握的ts高级特性
**觉得不错可以 star 这个 repo 关注更多内容。**
评论