跳至正文
Wener Notes

Typescript FAQ

约 4 分钟阅读
标签:FAQ
bash
# type checking
pnpm add -Dw @typescript/native-preview

# 无法排除 node_modules 问题 https://github.com/microsoft/TypeScript/issues/40426
npx tsc -p tsconfig.json --skipLibCheck --maxNodeModuleJsDepth 0 --noEmit

# 减少 node_modules 输出
npx tsc -p tsconfig.json --skipLibCheck --maxNodeModuleJsDepth 0 --noEmit | grep -v '/node_modules'

null

用 undefined . 不要用 null .

-- Coding Guidelines for Contributors to TypeScript.

tsconfig.tsbuildinfo

  • --incremental

type vs interface

建议优先 interface

  • 更直观 - 写法和功能上
  • 对熟悉其他语言的人来说更好理解
  • 弱于 type - 避免太复杂

  • type
    • Type alias
    • 可以表示任意类型
    • 支持 union、mapped type、conditional type、tuple
    • 隐含 Record<PropertyKey, unknown>
    • type 定义后就不可变了
  • interface
    • 可以 extends - 会比 type & 快一点
    • 同名会做合并
      • 通常是不希望的结果
      • 可以作为扩展点,库提供定义类型的能力

enum vs union

  • enum
    • 有类型,不能直接赋值
  • union
TypeScript
// 可以遍历的 ENUM
const permissions = ['read', 'write', 'execute'] as const;
type Permission = (typeof permissions)[number];

// 使用 ENUM 且可以遍历的方式
enum Permission {
  Read = 'r',
  Write = 'w',
  Execute = 'x',
}

const Permission = {
  Read: 'r',
  Write: 'w',
  Execute: 'x',
} as const;
type Permission = (typeof Permission)[keyof typeof Permission];

为每个方法添加一个参数

Expected 3 type arguments, but got 1

箭头函数使用泛型参数

arrow function use generic type parameter

TypeScript
const identity = <T>(value: T): T[] => {
  return [value];
};
TSX
// TSX
const identity = <T,>(arg: T): T => arg;

Path alias

  • 推荐 @/* -> src/*
    • 简单好配置
    • 适用于大多场景
  • 尽量保持使用相同的 alias 位置,让代码夸项目粘贴复制方便
tsconfig.json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  }
}
vite.config.ts
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    alias: {
      '@/': new URL('./src/', import.meta.url).pathname,
    },
  },
});
.swcrc
{
  "jsc": {
    "baseUrl": "./",
    "paths": {
      "@/*": ["@/*"]
    }
  }
}

ts-node

bash
npm add -D tsconfig-paths
依赖包
json
{
  "ts-node": {
    "require": ["tsconfig-paths/register"]
  }
}

I 接口命名前缀

  • 觉得需要用的时候就用
    • 类和接口同时存在
  • 如果只有接口没有实现时,不要加前缀
TypeScript
// user.ts
// IUserService 仅提供给使用方
export class UserService implements IUserService {}
export interface IUserService {}

// index.ts
// 对外只暴露接口,避免 import 的时候导入源码增加 bundle
export { type IUserService } from 'user.ts';

// client.ts
import type { IUserService } from 'server';
// 客户端不需要 bundle 源码
const svc = getService<IUserService>('user');

DEV

types.d.ts
declare var __DEV__: boolean;

使用 pnpm 安装,Typescript 报类型错误

  • 尝试 preserveSymlinks
json
{
  "compilerOptions": {
    "preserveSymlinks": true
  }
}

Types of property 'propTypes' are incompatible

method-signature-style

TypeScript
interface Example {
  // method shorthand syntax
  func(arg: string): number;

  // regular property with function type
  func: (arg: string) => number;
}

as const

bundle/rollup d.ts

The inferred type of '' cannot be named without a reference to sequelize

bash
# tsconfig 可以调整为 include 需要的内容
pnpm tsc -p ./tsconfig.json --noEmit false --emitDeclarationOnly true --declaration true --outDir ./dist/types

# 直接生成
pnpm tsc --target ESNext --jsx preserve --declaration --strict --pretty --out ./dist/server.js --module system --moduleResolution node --emitDeclarationOnly ./src/server/routers/_app.ts

Module 'wechat4u' resolves to an untyped module at , which cannot be augmented.

将所有的类型定义都放在 declare module 里

TS6133: 'React' is declared but its value is never read.

SyntaxError: This experimental syntax requires enabling one of the following parser plugin(s): "decorators", "decorators-legacy".

JavaScript
export default {
  importOrderParserPlugins: ['typescript', 'decorators-legacy'],
};

mixin

Polymorphic React Props

TypeScript
import * as React from "react";

type BoxProps<E extends ElementType> = Omit<ComponentProps<E>, 'as'> & {
  as?: E;
};

const Box = <E extends ElementType = 'div'>({ as, ...props }: BoxProps<E>) => {
  const TagName = as || 'div';
  return <TagName {...props} />;
};

type PolymorphicProps<P extends { as?: T }, T extends React.ElementType> = P &
  Omit<React.ComponentPropsWithoutRef<T>, keyof P>;

type ButtonProps<T extends React.ElementType> = PolymorphicProps<
  {
    as?: T;
    icon?: React.ReactNode;
    children?: React.ReactNode;
  },
  T
>;

function Button<T extends React.ElementType = "button">({
  as,
  ...props
}: ButtonProps<T>
) {
  const Component = as || "button";
  return <Component {...props} />;
}

NullableOptional

TypeScript
type RequiredKeys<T> = {
  [K in keyof T]-?: {} extends { [P in K]: T[K] } ? never : K;
}[keyof T];

type OptionalKeys<T> = {
  [K in keyof T]-?: {} extends { [P in K]: T[K] } ? K : never;
}[keyof T];

type PickRequired<T> = Pick<T, RequiredKeys<T>>;

type PickOptional<T> = Pick<T, OptionalKeys<T>>;

type Nullable<T> = {
  [P in keyof T]: T[P] | null;
};

type NullableOptional<T> = PickRequired<T> & Nullable<PickOptional<T>>;

SyntaxError: A type-only import can specify a default import or named bindings, but not both

TypeScript
import type React, { type ReactNode } from 'react';

TS2742

  • 当使 declaration
    时会要求
  • 当 composite
    时, 强制要求 declaration
    • 用于配合 references 使用

TS2742: The inferred type of X cannot be named without a reference to Y. This is likely not portable. A type annotation is necessary.

关联信息

反向链接、本文链接的其他页面和外部资料。

反向链接

  • trpc
    笔记 · bundle/rollup d.ts · bundlerollup dts

    trpc/trpc · @trpc/next - NextJS · spec · https://trpc.io/docs/rpc · 参考 · jlalmes/trpc-openapi · 使用 meta 定义路由 · - · 使用 zod 定义 input 和 output

参考资料

GitHub24 条
依赖包1 条
其他外链14 条
最近更新commit 11c4f58编辑