在TypeScrip中,我可以这样定义一个类型:

type Person = {
    givenName: string
    familyName: string
}

我可以为我的类型定义构造函数类型,如下所示:

type PersonConstructor = {
    new (givenName: string, familyName: string): Person
}

但我找不出构造函数实现的正确语法.以下代码无效,并产生错误:

const NamedPerson: PersonConstructor = (givenName: string, familyName: string) => {
    return { givenName, familyName }
}
Type '(givenName: string, familyName: string) => { givenName: string; familyName: string; }' is not assignable to type 'PersonConstructor'.
    Type '(givenName: string, familyName: string) => { givenName: string; familyName: string; }' provides no match for the signature 'new (givenName: string, familyName: string): Person'.ts(2322)":

我可以调用构造函数,没有问题,所以构造函数类型定义看起来不错,我认为:

const me = new NamedPerson('First', 'Last');

实现PersonConstructor的正确语法是什么?

推荐答案

实现construct signature的正确方法是使用class声明或表达式:

const NamedPerson: PersonConstructor = class {
  constructor(public givenName: string, public familyName: string) { }
}

const person = new NamedPerson("John", "Doe");
console.log(person.familyName.toUpperCase()) // "DOE"
console.log(person instanceof NamedPerson); // true

在上面的class表达式中,我使用了parameter properties来简化定义,但您不需要使用这个.


如果您想使用任意函数,则TypeScrip不会让您轻松做到这一点.ES5风格的构造函数在运行时工作(它们将属性赋给this,而不是返回任何东西),但TypeScrip不想添加对此的支持,建议您"使用classES":参见microsoft/TypeScript#2310.从构造函数返回值也可以在运行时使用,但TypeScrip目前不允许您将类构造函数标记为生成this类型以外的其他内容:有关功能请求,请参见microsoft/TypeScript#27594.而且TypeScrip并不能真正区分function expressions(可以使用new)和arrow functions(如果使用new就会在运行时爆炸,即使您从未try 在其中使用this),所以即使上面两个问题已经解决,您也需要注意您的函数不是错误类型的函数.

目前,要处理所有这些问题,需要使用某种具有type assertions的包装器函数来 suppress 编译器警告,并需要一个实现来使箭头函数可用.也许是这样的:

function toConstructor<A extends any[], R extends object>(
  fn: (...args: A) => R): new (...args: A) => R {
  return class {
    constructor(...args: any) {
      return fn(...args);
    }
  } as any;
}

然后你会像这样使用它

const NamedPerson: PersonConstructor =
  toConstructor((givenName: string, familyName: string) => {
    return { givenName, familyName }
  });

const person = new NamedPerson("John", "Doe");
console.log(person.familyName.toUpperCase()) // "DOE"
console.log(person instanceof NamedPerson); // false

哦,是的,person instanceof NamedPersonfalse,这很让人困惑.这是构造函数返回对象而不是赋值给this的缺点之一;原型没有正确设置.因此,即使类型脚本使使用任意函数作为类构造函数变得很容易,它们也会导致奇怪的行为.


如果您希望将运行时和编译器中的问题降到最低,则应该将类用作类,将函数用作函数,而不是将它们混合在一起.如果你有一个函数,那么你只要把它当作一个函数来使用就会更好,根本不涉及the new operator:

const namedPerson = (givenName: string, familyName: string): Person => {
  return { givenName, familyName }
}

const person = namedPerson("John", "Doe");
console.log(person.familyName.toUpperCase()) // "DOE"

Playground link to code

Typescript相关问答推荐

接口的额外属性作为同一接口的方法的参数类型'

对于合并对象中的可选属性(例如选项),类型推断可能是错误的

匿名静态类推断组成不正确推断

TypeScrip:逐个 case Select 退出noUncheck kedIndexedAccess

如何将所有props传递给React中的组件?

我可以使用TypeScrip从字符串词典/记录中填充强类型的环境对象吗?

如何创建一家Reduxstore ?

如何在ANGLE中注册自定义验证器

防止重复使用 Select 器重新渲染

刷新时保持当前名称的Angular

我在Angular 17中的environment.ts文件中得到了一个属性端点错误

打字错误推断

NPM使用Vite Reaction应用程序运行预览有效,但我无法将其部署到Netlify

Angular NG8001错误.组件只能在一个页面上工作,而不是在她的页面上工作

创建一个函数,该函数返回一个行为方式与传入函数相同的函数

编剧- Select 下拉框

如何在Next.js和Tailwind.css中更改悬停时的字体 colored颜色

如何调整项目数组,但允许权重影响顺序

如何在TypeScript中声明逆变函数成员?

Angular另一个组件的nativeelement未定义