gpt4 book ai didi

typescript - 表示具有私有(private)构造函数的泛型类的类型

转载 作者:行者123 更新时间:2023-12-05 03:17:17 25 4
gpt4 key购买 nike

我有一个简单的函数断言一个对象是一个类的实例。对象和类都作为参数传递给函数。

以下是一个说明问题的简化示例(没有深入了解真实代码的本质):

function verifyType<T>(instance: unknown, classType:new () => T):instance is T {
if (!(instance instanceof classType)) throw(`Expecting instance of ${classType.name}`);

return true;
}

( TypeScript Playground link )

此函数适用于大多数类:

class Foo {
constructor() {
}
}

const foo:unknown = new Foo();
verifyType(foo, Foo);
// OK

但是,如果该类具有私有(private)构造函数,我会收到编译器错误。

我理解其中的原因,这是有道理的。构造函数类型意味着调用代码可以构造类,而私有(private)构造函数意味着不允许这样做。

但是,我找不到仍然允许我使用 instanceof 的替代方案:

class Bar {
private constructor() {
}
static create() {
return new Bar();
}
}
const bar:unknown = Bar.create();
verifyType(bar, Foo);
// OK
verifyType(bar, Bar);
// Argument of type 'typeof Bar' is not assignable to parameter of type 'new () => Bar'.
// Cannot assign a 'private' constructor type to a 'public' constructor type.

尝试 T extends typeof Object

我读了How to refer to a class with private constructor in a function with generic type parameters?

根据对那个问题的回答,我想也许我可以使用以下内容:

function verifyType<T extends typeof Object>(instance: unknown, classType:T):instance is InstanceType<T>

但是,由于 Object 的许多静态方法,这似乎会引发错误:

const foo = new Foo();
verifyType(foo, Foo);
// Argument of type 'typeof Foo' is not assignable to parameter of type 'ObjectConstructor'.
// Type 'typeof Foo' is missing the following properties from type 'ObjectConstructor': getPrototypeOf, getOwnPropertyDescriptor, getOwnPropertyNames, create, and 16 more.

充满异国情调

我在 typeof Object 上尝试了很多变体,看看我是否既能满足 TypeScript 又能确保运行时的正确性,包括:

function verifyType<T extends Function & Pick<typeof Object, "prototype">>(instance: unknown, classType:T):instance is InstanceType<T>

然而,虽然这解决了编译时错误,但它似乎是通过允许在运行时失败的类型来实现的,这是 Not Acceptable :

verifyType(bar, ()=>{});
// No compile-time errors
// Runtime Error: Function has non-object prototype 'undefined' in instanceof check

帮帮我//@ts-expect-error,你是我唯一的希望

最后,这可能是一个过于小众的用例,我可能不得不接受这种边缘情况不会很快得到支持并相应地免除我的代码。

// @ts-expect-error Unfortunately, TS has no type representing a generic class with a private constructor
verifyType(bar, Bar);

最佳答案

我建议的方法是对 classType 参数的 prototype 属性进行推断,如下所示:

function verifyType<T extends object>(
instance: unknown,
classType: Function & { prototype: T }
): asserts instance is T {
if (!classType.prototype) throw (
`Wait, ${classType.name || "that"} is not a class constructor`);
if (!(instance instanceof classType)) throw (
`Expecting instance of ${classType.name}`);
}

一般来说,如果 y 是某种函数类型,TypeScript 会让您编写 x instanceof y,因此 Function &,然后我们推断 generic类型参数 T 对应于 classType 的实例类型,通过说它是 prototype 属性的类型(TypeScript 将类构造函数原型(prototype)建模为与类实例的类型相同,尽管在实践中通常并非如此。有关更多信息,请参阅 microsoft/TypeScript#44181 以及其他可能)。

除了 Function & {protoype: T} 之外,我还对您的代码进行了一些更改:

  • verifyType() 现在是 assertion function返回 asserts instance is T 而不是 type guard function返回 实例是 T。类型保护函数应该返回 truefalse 并且编译器在其 control flow analysis 中使用它.另一方面,断言函数不返回任何值,它们只是确保可缩小的东西确实缩小了。如果你有一个总是返回 true(否则抛出异常)的类型保护函数,那么你可能需要一个断言函数。

  • 我在主体内部进行了显式检查,以查看是否定义了 classType.prototype。 TypeScript 为 Function 提供了 the any typeprototype 属性而不是更安全的 unknown type ,不幸的是,这几乎不可能检测到像 ()=>{} 这样的非构造函数在成为 instanceof 的目标时会抛出运行时错误。那么,我没有尝试在类型系统中表示它,而是尝试强化 verifyType() 的实现以应对此类边缘情况。您可能需要检查 (!classType.prototype || typeof classType.prototype !== "object") 或您认为必要的任何其他逻辑。


让我们测试一下:

class Foo {
constructor() {
}
a = 1;
}
const foo: unknown = new Foo();
foo.a // error! Object is of type unknown
verifyType(foo, Foo);
foo.a // okay
console.log(foo.a.toFixed(1)) // "1.0"

看起来不错,编译器在调用 verifyType(foo, Foo) 之后(但不是之前)看到 foo 是一个 Foo .

class Bar {
private constructor() {
}
static create() {
return new Bar();
}
b = "z"
}
const bar: unknown = Bar.create();
verifyType(bar, Bar);
console.log(bar.b.toUpperCase()); // "Z"

看起来也不错。编译器很乐意允许 verifyType(bar, Bar) 因为 Bar 有一个类型为 Barprototype 属性(在编译器看来是这样的),因此您可以访问 Barb 属性。 Bar 有一个 private 构造函数并不重要。

最后:

const baz: unknown = new Date();
verifyType(baz, () => { }); // 💥 Wait, that is not a class constructor
baz // any

我们无法在编译时标记 verifyType(baz, ()=>{}),但至少你会得到一个有意义的运行时错误,因此随后的“缩小”bazunknownany(呃,谢谢 any)不会影响我们。

Playground link to code

关于typescript - 表示具有私有(private)构造函数的泛型类的类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74185545/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com