2022年已经过了三分之二,我才开始学TypeScript

284 阅读16分钟

我正在参与掘金创作者训练营第6期,点击了解活动详情

什么是TypeScript

引用官方定义:

Typed JavaScript at Any Scale.

添加了类型系统的 JavaScript,适用于任何规模的项目。

特性

类型系统,弥补了JavaScript的缺点

我们都知道,JavaScript中是没有类型可言的。

  • 一个变量定义的时候是 string类型, 可能一会就变成了 number类型或者其他的类型
  • JavaScript隐式类型转换,有些变量在运行前很难明确类型。比如下面的例子
const { log } = console
let foo = '123'
foo = 456 // 类型改变

const sum = foo + '789'
log('sum', sum, typeof sum)

const bool = 0
// bool会被当成false
if (bool) {
  log(true)
} else {
  log(false)
}

image.png

可以看出JavaScript是很灵活的,这使得JavaScript发展迅速,同时也带来了很多问题:

  • 项目不易维护
  • 代码质量参差不齐
  • 运行时错误多

所以说Typescript弥补了JavaScript的缺点

任何规模的项目适用

这是显而易见的,随着项目复杂度,项目功能的增加,TypeScript能带来更好的可维护性。

静态类型,编译阶段就会进行类型检查

JavaScript是动态类型,在运行阶段执行类型检查,因为JavaScript是一门解释型语言,没有编译阶段,这段代码在运行时才会报错:

const f = 0
f.split('')

image.png

但如果在ts中这么写,就会是下面这样:

image.png

在编写代码的时候就把一些明显的错误提示出来了。很完美😊

弱类型

为什么说Typescript是弱类型的,看下面的例子: image.png

变量f初始化值为0,是number类型,与空字符串相加后,结果的类型为string,说明 + 被识别为字符串拼接,与JavaScript一致。

TypeScript 是完全兼容 JavaScript 的,它不会修改 JavaScript 运行时的特性,所以它们都是弱类型

TS 文件

  • 文件后缀为.ts
  • 文件后缀为.tsx (用 TypeScript 编写 React)

运行:

  • ts文件不能直接运行,要先编译为js文件,才能运行。
  • tsx文件也要通过插件处理编译后才能运行。

安装TypeScript

yarn add typescript -g // 全局安装

ts文件不能直接运行, 将ts文件编译为js文件,再调用node才能执行

编译

  • tsc 编译当前目录下的所有ts文件
  • tsc ts文件 编译指定的文件

编译配置文件

快速生成配置文件:

tsc --init

执行后会在当前页面生成一个tsconfig.json的配置文件

配置项如下:

{
  "compilerOptions": {
    /* Visit https://aka.ms/tsconfig to read more about this file */

    /* Projects */
    // "incremental": true,                              /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
    // "composite": true,                                /* Enable constraints that allow a TypeScript project to be used with project references. */
    // "tsBuildInfoFile": "./.tsbuildinfo",              /* Specify the path to .tsbuildinfo incremental compilation file. */
    // "disableSourceOfProjectReferenceRedirect": true,  /* Disable preferring source files instead of declaration files when referencing composite projects. */
    // "disableSolutionSearching": true,                 /* Opt a project out of multi-project reference checking when editing. */
    // "disableReferencedProjectLoad": true,             /* Reduce the number of projects loaded automatically by TypeScript. */

    /* Language and Environment */
    "target": "ES2015",                                  /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
    "lib": ["ES2017", "DOM"],                                        /* Specify a set of bundled library declaration files that describe the target runtime environment. */
    // "jsx": "preserve",                                /* Specify what JSX code is generated. */
    // "experimentalDecorators": true,                   /* Enable experimental support for TC39 stage 2 draft decorators. */
    // "emitDecoratorMetadata": true,                    /* Emit design-type metadata for decorated declarations in source files. */
    // "jsxFactory": "",                                 /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
    // "jsxFragmentFactory": "",                         /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
    // "jsxImportSource": "",                            /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
    // "reactNamespace": "",                             /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
    // "noLib": true,                                    /* Disable including any library files, including the default lib.d.ts. */
    // "useDefineForClassFields": true,                  /* Emit ECMAScript-standard-compliant class fields. */
    // "moduleDetection": "auto",                        /* Control what method is used to detect module-format JS files. */

    /* Modules */
    "module": "CommonJS",                                /* Specify what module code is generated. */
    "rootDir": "./",                                  /* Specify the root folder within your source files. */
    // "moduleResolution": "node",                       /* Specify how TypeScript looks up a file from a given module specifier. */
    // "baseUrl": "./",                                  /* Specify the base directory to resolve non-relative module names. */
    // "paths": {},                                      /* Specify a set of entries that re-map imports to additional lookup locations. */
    // "rootDirs": [],                                   /* Allow multiple folders to be treated as one when resolving modules. */
    // "typeRoots": [],                                  /* Specify multiple folders that act like './node_modules/@types'. */
    // "types": [],                                      /* Specify type package names to be included without being referenced in a source file. */
    // "allowUmdGlobalAccess": true,                     /* Allow accessing UMD globals from modules. */
    // "moduleSuffixes": [],                             /* List of file name suffixes to search when resolving a module. */
    // "resolveJsonModule": true,                        /* Enable importing .json files. */
    // "noResolve": true,                                /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */

    /* JavaScript Support */
    // "allowJs": true,                                  /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
    // "checkJs": true,                                  /* Enable error reporting in type-checked JavaScript files. */
    // "maxNodeModuleJsDepth": 1,                        /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */

    /* Emit */
    "declaration": true,                              /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
    // "declarationMap": true,                           /* Create sourcemaps for d.ts files. */
    // "emitDeclarationOnly": true,                      /* Only output d.ts files and not JavaScript files. */
    // "sourceMap": true,                                /* Create source map files for emitted JavaScript files. */
    // "outFile": "./",                                  /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
    "outDir": "dist",                                   /* Specify an output folder for all emitted files. */
    // "removeComments": true,                           /* Disable emitting comments. */
    // "noEmit": true,                                   /* Disable emitting files from a compilation. */
    // "importHelpers": true,                            /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
    // "importsNotUsedAsValues": "remove",               /* Specify emit/checking behavior for imports that are only used for types. */
    // "downlevelIteration": true,                       /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
    // "sourceRoot": "",                                 /* Specify the root path for debuggers to find the reference source code. */
    // "mapRoot": "",                                    /* Specify the location where debugger should locate map files instead of generated locations. */
    // "inlineSourceMap": true,                          /* Include sourcemap files inside the emitted JavaScript. */
    // "inlineSources": true,                            /* Include source code in the sourcemaps inside the emitted JavaScript. */
    // "emitBOM": true,                                  /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
    // "newLine": "crlf",                                /* Set the newline character for emitting files. */
    // "stripInternal": true,                            /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
    // "noEmitHelpers": true,                            /* Disable generating custom helper functions like '__extends' in compiled output. */
    // "noEmitOnError": true,                            /* Disable emitting files if any type checking errors are reported. */
    // "preserveConstEnums": true,                       /* Disable erasing 'const enum' declarations in generated code. */
    // "declarationDir": "./",                           /* Specify the output directory for generated declaration files. */
    // "preserveValueImports": true,                     /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */

    /* Interop Constraints */
    // "isolatedModules": true,                          /* Ensure that each file can be safely transpiled without relying on other imports. */
    // "allowSyntheticDefaultImports": true,             /* Allow 'import x from y' when a module doesn't have a default export. */
    "esModuleInterop": true,                             /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
    // "preserveSymlinks": true,                         /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
    "forceConsistentCasingInFileNames": true,            /* Ensure that casing is correct in imports. */

    /* Type Checking */
    "strict": true,                                      /* Enable all strict type-checking options. */
    // "noImplicitAny": true,                            /* Enable error reporting for expressions and declarations with an implied 'any' type. */
    // "strictNullChecks": true,                         /* When type checking, take into account 'null' and 'undefined'. */
    // "strictFunctionTypes": true,                      /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
    // "strictBindCallApply": true,                      /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
    // "strictPropertyInitialization": true,             /* Check for class properties that are declared but not set in the constructor. */
    // "noImplicitThis": true,                           /* Enable error reporting when 'this' is given the type 'any'. */
    // "useUnknownInCatchVariables": true,               /* Default catch clause variables as 'unknown' instead of 'any'. */
    // "alwaysStrict": true,                             /* Ensure 'use strict' is always emitted. */
    // "noUnusedLocals": true,                           /* Enable error reporting when local variables aren't read. */
    // "noUnusedParameters": true,                       /* Raise an error when a function parameter isn't read. */
    // "exactOptionalPropertyTypes": true,               /* Interpret optional property types as written, rather than adding 'undefined'. */
    // "noImplicitReturns": true,                        /* Enable error reporting for codepaths that do not explicitly return in a function. */
    // "noFallthroughCasesInSwitch": true,               /* Enable error reporting for fallthrough cases in switch statements. */
    // "noUncheckedIndexedAccess": true,                 /* Add 'undefined' to a type when accessed using an index. */
    // "noImplicitOverride": true,                       /* Ensure overriding members in derived classes are marked with an override modifier. */
    // "noPropertyAccessFromIndexSignature": true,       /* Enforces using indexed accessors for keys declared using an indexed type. */
    // "allowUnusedLabels": true,                        /* Disable error reporting for unused labels. */
    // "allowUnreachableCode": true,                     /* Disable error reporting for unreachable code. */

    /* Completeness */
    // "skipDefaultLibCheck": true,                      /* Skip type checking .d.ts files that are included with TypeScript. */
    "skipLibCheck": true                                 /* Skip type checking all .d.ts files. */
  }
}

基础

在 TypeScript 中,使用 : 指定变量的类型,: 的前后有没有空格都可以。

基础数据类型

JS 中基础数据类型包括:booleannumberstringnullundefined 以及 ES6 中的新类型 Symbol 和 ES10 中的新类型 BigInt

对象类型

对象类型包括:Object、Array、function、Set、Map

boolean

// 推荐
const bool: boolean = true
// 或者
const bool1: boolean = Boolean(1)

⚠️ 在 TypeScript 中,boolean 是 JavaScript 中的基本类型,而 Boolean 是 JavaScript 中的构造函数。其他基本类型(除了 null 和 undefined)一样

举个🌰,下面的写法编译不通过:

const bool1: boolean = new Boolean(1) // 错误的

image.png

number

const num:number = 2
const nan: number = NaN;
const infinity: number = Infinity;
// 二进制
const binary: number = 0b1010 // 10
// 八进制
const octal: number = 0o020 // 16

string

const str: string = 'haha'
const templateStr = `${str},你好`

null 和 undefined

⚠️在非严格模式下,nullundefined可以赋值给其他的类型。比如:

const nu:null = null
const undef: undefined = undefined

let str: string = ''
let bool: boolean = false
let num: number = null

str = nu
bool = nu
num = nu

str = undef
bool = undef
num = undef

严格模式下上面的代码编译不通过。

❓如何配置为非严格模式

tsconfig.json文件中,添加下列配置。

{
  "compilerOptions": {
    "strictNullChecks": false
  }
}

void

void 用来表示函数没有任何返回值。比如:

// 用void 表示printName函数没有任何返回值
function printName(name: string): void{
  console.log(name)
}

// 指定返回值为string
function getStrName(name: string): string{
  return 'STR' + name
}

还可以声明 void 类型的变量,但没什么用,只能赋值为 null 或者 undefined

any

任意值(any)用来表示允许赋值为任意类型。

定义为 any 类型代表着与在JavaScript中定义变量一样,想怎么写就怎么写

let a:any = 20

a = ''
a = true
a = null
// 访问任意属性
a.s = 4
// 访问任意方法
a.say()

unknown

unknown也代表着 任意值,类似于any 但是比any安全

let unk: unknown = 0

image.png

never

可用于表示函数抛出异常:

function fail(msg: string): never {

throw new Error(msg);

}

或者表示我们想不到的,比如官网的🌰: image.png

类型推论

如果我们在定义变量时没有为变量指定明确的类型,TypeScript将会根据类型推论的规则去推断出一个类型。

举个简单的🌰:

定义noType变量时,没有明确指定其类型,然后推理的类型为number,当我们把一个string 赋值给 noType 就会报错 image.png

联合类型

联合类型指:一个变量的值可以是多个类型中的任意一种。

举个简单的🌰:

// 联合类型
// strOrNum变量既可以为string,又可以为number
let strOrNum: string | number = 20

strOrNum = ''

strOrNum = 300

image.png

interface(接口/对象的类型)

interface Person {
  readonly id: string, // id只能读取值,不能更改
  name: string, // 必选项, 即定义的对象中必须有name属性
  age: number, // 必选项
  isMarry: boolean, // 必选项
  height?: number, // 可选项
  [propName: string]: any // 可扩展,并且扩展项的值可以是任意类型
}

const tom: Person = {
  id: '0',
  name: 'tom',
  age: 12,
  isMarry: false,
  // 扩展项
  extA: '',
  extB: 2,
  extC: true
}

⚠️Personname age isMarry是必选项,也就是我们创建的Person类型的对象中必须包含这三个属性。否则编译将不通过。

image.png

⚠️如果更改tom 对象的 id 属性,会报以下错误:

image.png

⚠️当interface定义了可以有任意属性,就是指配置了[propName: string]: 类型,意味着接口中的其他属性的类型都必须是 任意属性的类型或其子类型。在上例中任意属性的类型为any,满足这一要求。举个不满足的🌰:

image.png

数组的类型

// 用类型加方括号
const arr: number[] = [1, 2, 3]

// 用数组泛型表示数组
const arr1: Array<number> = [1, 2, 3]

// 用接口表示数组
interface MyArray {
  [index: number] : number
}

const myarr: MyArray = [1, 2, 3]

// 类数组
interface LikeArray {
  [index: number] : any,
  length: number,
  [propName: string]: any
}

function func(): void {
  const argu:LikeArray = arguments
}

函数的类型

函数声明

 // 函数声明
function  testFunc1(a: string, b: string): boolean {
  return a === b
}

函数表达式

// 自动推理类型
const expessionFunc1 = function (a: string, b: string): boolean {
  return a === b
}
// 手动添加类型
const expessionFunc2: (a: string, b: string) => boolean = function (a: string, b: string): boolean {
  return a === b
}

81311deef7ac1e079107fa9d438dd30.jpg ⚠️调用时,参数比定义时多 或 少都会编译不通过

image.png

可选参数

function funcRestArgu(first: string, two?: number) {

}

⚠️可选参数只能在固定参数的后面 image.png

剩余参数

function funcRestArgu(first: string, two?: number, ...rest: string[]) {

}

⚠️剩余参数只能放在最后 image.png

类型断言

语法:

  • 变量 as 类型 👍推荐
  • <类型>变量

❓为什么不推荐第二种写法呢,因为在tsx<ddd> 表示一个ReactNode, 为了防止冲突。

类型断言有啥用

将联合类型断言为其中一个类型 image.png

将父类型断言为一个子类型

interface HttpErrorOptions {
  httpCode: number,
  [propName: string]: any
}

interface OtherErrorOptions {
  errorMsg: string,
  [propName: string]: any
}
class HttpError extends Error {
  httpCode: number

  constructor(options: HttpErrorOptions) {
    super()
    this.httpCode = options.httpCode
  }
}

class OtherError extends Error {
  errorMsg: string

  constructor(opts: OtherErrorOptions) {
    super()
    this.errorMsg = opts.errorMsg
  }
}

function isHttpError(error: Error) {
  return (error as HttpError).httpCode !== 200
}

将任何一个类型断言为any

image.png

(window as any).x = 20

声明文件

当使用第三方库时,我们需要引用它的声明文件,才能获得对应的代码补全、接口提示等功能。

当我们需要使用一个第三方的npm包的时候,它的声明文件一般可能存在于两个地方:

  1. 与该 npm 包绑定在一起。判断依据是 package.json 中有 types 字段,或者有一个 index.d.ts 声明文件。这种模式不需要额外安装其他包,是最为推荐的,所以以后我们自己创建 npm 包的时候,最好也将声明文件与 npm 包绑定在一起。
  2. 发布到 @types 里。我们只需要尝试安装一下对应的 @types 包就知道是否存在该声明文件,安装命令是 npm install @types/foo --save-dev。这种模式一般是由于 npm 包的维护者没有提供声明文件,所以只能由其他人将声明文件发布到 @types 里了。

假如以上两种方式都没有找到对应的声明文件,那么我们就需要自己为它写声明文件了。由于是通过 import 语句导入的模块,所以声明文件存放的位置也有所约束,一般有两种方案:

  1. 创建一个 node_modules/@types/foo/index.d.ts 文件,存放 foo 模块的声明文件。这种方式不需要额外的配置,但是 node_modules 目录不稳定,代码也没有被保存到仓库中,无法回溯版本,有不小心被删除的风险,故不太建议用这种方案,一般只用作临时测试。
  2. 创建一个 types 目录,专门用来管理自己写的声明文件,将 foo 的声明文件放到 types/foo/index.d.ts 中。这种方式需要配置下 tsconfig.json 中的 paths 和 baseUrl 字段。

npm 包的声明文件

npm 包的声明文件主要有以下几种语法:

  • export 导出变量
  • export namespace 导出(含有子属性的)对象
  • export default ES6 默认导出
  • export = commonjs 导出模块

export

// type.d.ts
export interface Person {
  readonly id: string // id只能读取值,不能更改
  name: string // 必选项, 即定义的对象中必须有name属性
  age: number // 必选项
  isMarry: boolean // 必选项
  height?: number // 可选项
  [propName: string]: any // 可扩展,并且扩展项的值可以是任意类型
}

export function getName():void

export const strOrNum: string | number

export enum week{Mon, Tus = 100, Wed, Thu, Fri, Sat, Sun}

export class People{
  name: string
  private readonly age: number
  constructor()
  getAge(): number
}

使用的时候

import { Person, People, week, strOrNum, getName } from './type'

export namespace

export namespace 用来导出一个拥有子属性的对象

export default

在 ES6 模块系统中,使用 export default 可以导出一个默认值。只有 functionclass 和 interface 可以直接默认导出,其他的变量需要先定义出来,再默认导出。

export =

对于使用 commonjs 规范的库,假如要为它写类型声明文件的话,就需要使用到 export = 语法

export = foo

declare namespace foo {
  const name: string
  function getName(): string
}

使用时推荐的用法:

import foo = require('./commonjs')

自动生成声明文件

如果库的源码本身就是由 ts 写的,那么在使用 tsc 脚本将 ts 编译为 js 的时候,添加 declaration 选项,就可以同时也生成 .d.ts 声明文件了。

我们可以在命令行中添加 --declaration(简写 -d),或者在 tsconfig.json 中添加 declaration 选项。这里以 tsconfig.json 为例:

{
  "compilerOptions": {
      "declaration": true,
      "outDir": "dist",
  }
}

image.png

执行tsc命令后

image.png

进阶

类型别名

语法: type 类型。相当于一个类型的简写,常用于联合类型 image.png

字符串字面量类型

用来约束取值只能是某几个字符串中的一个。

语法: type 类型。

type AorB = 'A' | 'B'

let h: AorB = 'B'

枚举 enum

用于取值被限定在一定范围内的场景, 🌰:

一周有七天:

enum week {Mon, Tus, Wed, Thu, Fri, Sat, Sun}

❓编译过后什么样

var week;
(function (week) {
    week[week["Mon"] = 0] = "Mon";
    week[week["Tus"] = 1] = "Tus";
    week[week["Wed"] = 2] = "Wed";
    week[week["Thu"] = 3] = "Thu";
    week[week["Fri"] = 4] = "Fri";
    week[week["Sat"] = 5] = "Sat";
    week[week["Sun"] = 6] = "Sun";
})(week || (week = {}));

就相当于这样的一个对象:

image.png

所以代码中我们可以这么用

week.Mon // 0
week[0] // 'Mon'
// ...

枚举成员会被赋值为从 0 开始递增的数字,同时也会对枚举值到枚举名进行反向映射,也可以手动赋值

为枚举成员手动赋值

// 赋值为常数
enum week {Mon, Tus = 100, Wed, Thu, Fri, Sat, Sun}

// 赋值为计算的值
enum week1 {Mon, Tus, Wed, Thu, Fri, Sat, Sun = h.length}

// 如果不是最后一个枚举项 被 赋值为计算的值,那这个枚举项后面的一项必须手动赋值,否则编译不通过
enum week2 {Mon, Tus = h.length, Wed = 2, Thu, Fri, Sat, Sun}

编译后:

image.png

常数枚举

const enum 定义的枚举类型:

const enum constweek { Mon = 20, Tus, Wed, Thu, Fri, Sat, Sun }
let sun = constweek.Sun

它会在编译阶段被删除,并且不能包含计算成员。

编译过后:

var sun = 26 /* constweek.Sun */;

外部枚举

declare enum 定义的枚举类型:

declare enum color {red, green = 1, blue}

它会在编译阶段被删除,并且不能包含计算成员。

元组

数组中是相同类型的对象,元组则是不同类型的对象。

let xx: [string, number] = ['', 2]

// 可以添加项,可添加的项是元组中每个类型的联合类型,按照上例来说就是 string | number
xx.push(5)

class

关于类的介绍,请参考Class 的基本语法

TypeScript中可以使用public private proteced来修饰类的属性,方法和构造函数

  • public 修饰的属性或方法是公有的,可以在任何地方被访问到,默认所有的属性和方法都是 public 的
  • private 修饰的属性或方法是私有的,不能在声明它的类的外部访问,只能在类本身访问
  • protected 修饰的属性或方法是受保护的,它和 private 类似,区别是它在子类中也是允许被访问的

看个🌰:

image.png

private constructor

当用private修饰构造函数,那这个类不能被实例化,也不能被继承

image.png

protected constructor

当用protected修饰构造函数,那这个类不能被实例化,可以被继承

image.png

抽象类

abstract修饰的类,只能被继承,不能被实例化。

image.png

泛型

泛型就是指在定义接口,函数,类的时候不明确定义类型,使用泛型代替,等真正用的时候才明确类型的一种特性。

单个类型参数

// 单个类型参数
function getArray<T>(arrayItem: T): Array<T> {
  return [arrayItem]
}

多个类型参数

// 多个类型参数
function moreArgu<T, U>(argu1: T, argu2: U): [T, U]{
  return [argu1, argu2]
}

const val1 = getArray(2)

const val2 = moreArgu('1', 2)

泛型定义接口中的函数

// 接口中的函数也可以使用泛型来定义
interface X{
  getArr<T>(argu1: T): Array<T>
}

const r: X = {
  getArr(argu1){
    return [argu1]
  }
}
const val3 = r.getArr('')

泛型类

// 泛型类
class M<T> {
  getArr: (argu1: T) => Array<T>
  constructor() {
    this.getArr = (argu1) => [argu1]
  }
}

const m = new M()
const mArr = m.getArr('1')

为泛型指定默认类型

// 默认类型
function defaultType<T = number>(arrayItem: T): Array<T> {
  return [arrayItem]
}

泛型约束

在函数内部使用泛型变量的时候,由于事先不知道它是哪种类型,所以不能随意的操作它的属性或方法,如果需要操作一些属性和方法,就可以通过以下的示例使用泛型约束来实现。

❌ 编译不通过

function ErrH<T>(argu1: T): Array<T> {
  if (argu1.length) {
    return new Array(argu1.length).fill(argu1)
  }
  return [argu1]
}

✅ 使用泛型约束

function H<T extends LikeArray>(argu1: T): Array<T> {
  if (argu1.length) {
    return new Array(argu1.length).fill(argu1)
  }
  return [argu1]
}

参考

TypeScript 入门教程 (xcatliu.com)