Is there a way to add correct type annotations for this TypeScript code with an inline function class?

huangapple go评论70阅读模式
英文:

Is there a way to add correct type annotations for this TypeScript code with an inline function class?

问题

可以添加哪些类型注解来通知 TypeScript 此代码是正确的?

是的,我确实需要内联函数类。此代码已从我的实际问题简化为此简化版本。

let x: number = 10;
const obj: { constructor(): any } = new (function() {
if (--x) return this.constructor();
return {};
});

'this' 隐式具有类型 'any',因为它没有类型注解.ts(2683)
'new' 表达式的目标缺乏构造函数签名,隐式具有类型 'any'。ts(7009)


我尝试完全注释所有内容,但这并不能解决问题。以下是示例:

type myType = { constructor(): myType };
let x: number = 10;
const obj: myType = new (function(): myType {
if (--x) return (this as myType).constructor();
return {} as myType;
});

'new' 表达式的目标缺乏构造函数签名,隐式具有类型 'any'。ts(7009)


我是否遗漏了 TypeScript 文档中的某些内容?似乎没有任何通用的构造函数类型,并且构造函数类型似乎并不适用于构造函数。
英文:

What type annotations can I add to inform TypeScript that this code is correct?

Yes I do need that inline function class. This code has been reduced from my actual problem to this simplified version.

let x = 10;
const obj = new (function() {
  if(--x) return this.constructor();
  return {};
});
'this' implicitly has type 'any' because it does not have a type annotation.ts(2683)
'new' expression, whose target lacks a construct signature, implicitly has an 'any' type.ts(7009)

I tried totally annotating everything and that doesn't fix it. Here is what that looks like:

type myType = { constructor(): myType };
let x: number = 10;
const obj: myType = new (function(): myType {
  if(--x) return (this as myType).constructor();
  return {} as myType;
});
'new' expression, whose target lacks a construct signature, implicitly has an 'any' type.ts(7009)

Is there something in the TypeScript docs I'm missing? There don't seem to be any Constructor types that are generic and the Constructor types don't really appear to be for constructors.

答案1

得分: 1

如果你想说一个类型是可"newable"的,或者是new 操作符的有效目标,你应该使用一个构造函数签名。构造函数签名可以写成{ new (⋯): ⋯},就像一个调用签名,或者写成new (⋯) => ⋯,就像一个箭头函数表达式。

以下是一种编写代码以使其编译的方法:

let x = 10;
type MyType = { new(): {} };
const obj = new ((function (this: {}) {
  console.log(x);
  if (--x) return this.constructor();
  return {};
}) as any as MyType);

这里我们将 MyType 定义为 { new(): {} },一个没有构造函数参数并产生类型为 {} 的构造函数签名。

然后我们通过 (⋯) as any as MyType 断言了匿名函数表达式的类型为 MyType。请注意,(⋯) as MyType 会失败,因为TypeScript 不认为可调用类型和可实例化类型之间的关系足够密切以允许它们之间的直接断言。(在现代 JavaScript 中,大多数类构造函数如果你尝试在没有 new 的情况下调用它们,它们会报错,而箭头函数如果你尝试用 new 调用它们而不是调用它们,它们会报错,所以 TypeScript 故意将它们分开。)你需要一个中间的东西,被认为与两者都相关;我选择了 any 类型,但你也可以选择其他的,比如 Function

另外,我给了匿名函数一个类型为 {}this 参数,这样编译器在函数体内处理 this 时会很高兴。

总之,现在没有错误了,obj 的值被推断为类型 {}。显然在你实际的用例中,你需要找出符合你需求的类型,但假设无论那些是,它们都会涉及至少一个构造函数签名。

英文:

If you want to say that a type is "newable", or a valid target for the new operator, you should use a construct signature. A construct signature can be written either as { new (⋯): ⋯} like a call signature or as new (⋯) => ⋯ like an arrow function expression.

Here's one way to write your code so it compiles:

let x = 10;
type MyType = { new(): {} };
const obj = new ((function (this: {}) {
  console.log(x);
  if (--x) return this.constructor();
  return {};
}) as any as MyType);

Here we've defined MyType as { new(): {} }, a construct signature that takes no constructor arguments and produces a value of type {}.

Then we've asserted that the anonymous function expression is of type MyType via (⋯) as any as MyType. Note that (⋯) as MyType fails because TypeScript doesn't see callable types and newable types as related enough to allow direct assertions between them. (In modern JavaScript, most class constructors will complain if you try to call them without new, and arrow functions complain if you try to new them instead of calling them, so TypeScript separates these on purpose.) You need something intermediate that is seen as related to both; I chose the any type but you could something else, like Function for example.

Also I gave the anonymous function a this parameter of type {}, so that the compiler would be happy manipulating this inside the function body.

Anyway, now there is no error, and the value of obj is inferred as being of type {}. Obviously in your actual use case you'll need to figure out the typings that meet your needs, but presumably whatever those are they will involve at least one construct signature.

Playground link to code

答案2

得分: 0

将其从(...a: A) => B类型映射到new (...a: A) => B的通用映射:

function fakeNewable<F extends (...a: any) =&gt; any>(
  fn: F,
): (new (...a: Parameters<F>) =&gt; ReturnType<F>) /* ` &amp; F` if you also need other props */ {
  return fn as any
} 
function foo() {
  return Math.random() &gt; 0.5 ? 'foo' : { foo: 'bar' };
}
let v = new (fakeNewable(foo))
//  ^?
// let v: string | { foo: string; }

(请记住,如果在new调用中返回undefined,您将得到构造的对象)

英文:

Make a generic to map it from (...a: A) =&gt; B type to new (...a: A) =&gt; B

function fakeNewable&lt;F extends (...a: any) =&gt; any&gt;(
  fn: F,
): (new (...a: Parameters&lt;F&gt;) =&gt; ReturnType&lt;F&gt;) /* ` &amp; F` if you also need other props */ {
  return fn as any
} 
function foo() {
  return Math.random() &gt; 0.5 ? &#39;foo&#39; : { foo: &#39;bar&#39; };
}
let v = new (fakeNewable(foo))
//  ^?
// let v: string | { foo: string; }

(remember, that if you return undefined inside new call, you'll get the constructed object)

huangapple
  • 本文由 发表于 2023年5月28日 14:41:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/76350260.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定