- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
例如,我制作了一个名为 lowclass
的 JavaScript 库我想知道如何让它在 TypeScript 类型系统中工作。
该库允许我们通过将对象文字传递到 API 来定义一个类,如下所示,我想知道如何让它返回一个与编写常规 类有效相同的类型{}
:
import Class from 'lowclass'
const Animal = Class('Animal', {
constructor( sound ) {
this.sound = sound
},
makeSound() { console.log( this.sound ) }
})
const Dog = Class('Dog').extends(Animal, ({Super}) => ({
constructor( size ) {
if ( size === 'small' )
Super(this).constructor('woof')
if ( size === 'big' )
Super(this).constructor('WOOF')
},
bark() { this.makeSound() }
}))
const smallDog = new Dog('small')
smallDog.bark() // "woof"
const bigDog = new Dog('big')
bigDog.bark() // "WOOF"
如您所见,Class()
和 Class().extends()
API 接受用于定义类的对象文字。
我如何输入此 API,以便最终结果是 Animal
和 Dog
在 TypeScript 中的行为就像我使用原生 class Animal { }
和 class Dog extends Animal{}
语法?
也就是说,如果我要将代码库从 JavaScript 切换到 TypeScript,在这种情况下我应该如何输入 API,以便最终结果是使用我用 lowclass 制作的类的人可以像使用常规类一样使用它们?
EDIT1:这似乎是一种简单的方法来键入我用 lowclass 创建的类,方法是用 JavaScript 编写它们并在 .d.ts
类型中声明常规 class {}
定义定义文件。将我的低级代码库转换为 TypeScript 似乎更加困难,甚至可能,这样它就可以在定义类时自动输入,而不是为每个类创建 .d.ts
文件。
EDIT2:我想到的另一个想法是我可以保留 lowclass 不变(JavaScript 类型为 any
),然后当我定义类时,我可以使用 as SomeType 来定义它们
其中 SomeType
可以是同一文件内的类型声明。这可能比让 lowclass 成为 TypeScript 库以便类型是自动的更不枯燥,因为我必须重新声明我在使用 lowclass API 时已经定义的方法和属性。
最佳答案
好吧,我们需要解决几个问题才能使它以与 Typescript 类类似的方式工作。在我们开始之前,我在 Typescript 中完成以下所有编码 strict
模式,如果没有它,某些键入行为将无法工作,如果您对解决方案感兴趣,我们可以确定所需的特定选项。
在 typescript 中,类占有特殊的位置,因为它们代表一个值(构造函数是一个 Javascript 值)和一个类型。 const
您定义的仅代表值(构造函数)。具有 Dog
的类型例如我们需要显式定义 Dog
的实例类型让它稍后可用:
const Dog = /* ... */
type Dog = InstanceType<typeof Dog>
const smallDog: Dog = new Dog('small') // We can now type a variable or a field
第二个问题是constructor
是一个简单的函数,不是构造函数, typescript 不会让我们调用 new
在一个简单的函数上(至少不是在严格模式下)。为了解决这个问题,我们可以使用条件类型在构造函数和原始函数之间进行映射。该方法类似于 here但为了简单起见,我将只为几个参数编写它,您可以添加更多参数:
type IsValidArg<T> = T extends object ? keyof T extends never ? false : true : true;
type FunctionToConstructor<T, TReturn> =
T extends (a: infer A, b: infer B) => void ?
IsValidArg<B> extends true ? new (p1: A, p2: B) => TReturn :
IsValidArg<A> extends true ? new (p1: A) => TReturn :
new () => TReturn :
never;
使用上面的类型,我们现在可以创建简单的 Class
函数将接受对象文字并构建一个看起来像声明的类的类型。如果这里没有constructor
字段,我们将假设一个空的构造函数,我们必须删除 constructor
根据我们将返回的新构造函数返回的类型,我们可以用 Pick<T, Exclude<keyof T, 'constructor'>>
来做到这一点.我们还将保留一个字段 __original
拥有对象字面量的原始类型,稍后会有用:
function Class<T>(name: string, members: T): FunctionToConstructor<ConstructorOrDefault<T>, Pick<T, Exclude<keyof T, 'constructor'>>> & { __original: T }
const Animal = Class('Animal', {
sound: '', // class field
constructor(sound: string) {
this.sound = sound;
},
makeSound() { console.log(this.sound) // this typed correctly }
})
在Animal
以上声明,this
在类型的方法中正确键入,这很好并且适用于对象文字。对于对象文字 this
将具有对象文字中定义的函数中当前对象的类型。问题是我们需要指定 this
的类型扩展现有类型时,如 this
将具有当前对象文字的成员加上基类型的成员。幸运的是,typescript 让我们可以使用 ThisType<T>
来做到这一点编译器使用并描述的标记类型 here
现在使用上下文 this,我们可以创建 extends
功能,唯一要解决的问题是我们需要查看派生类是否有自己的构造函数,或者我们可以使用基类构造函数,用新类型替换实例类型。
type ReplaceCtorReturn<T, TReturn> =
T extends new (a: infer A, b: infer B) => void ?
IsValidArg<B> extends true ? new (p1: A, p2: B) => TReturn :
IsValidArg<A> extends true ? new (p1: A) => TReturn :
new () => TReturn :
never;
function Class(name: string): {
extends<TBase extends {
new(...args: any[]): any,
__original: any
}, T>(base: TBase, members: (b: { Super : (t: any) => TBase['__original'] }) => T & ThisType<T & InstanceType<TBase>>):
T extends { constructor: infer TCtor } ?
FunctionToConstructor<ConstructorOrDefault<T>, InstanceType<TBase> & Pick<T, Exclude<keyof T, 'constructor'>>>
:
ReplaceCtorReturn<TBase, InstanceType<TBase> & Pick<T, Exclude<keyof T, 'constructor'>>>
}
综合起来:
type IsValidArg<T> = T extends object ? keyof T extends never ? false : true : true;
type FunctionToConstructor<T, TReturn> =
T extends (a: infer A, b: infer B) => void ?
IsValidArg<B> extends true ? new (p1: A, p2: B) => TReturn :
IsValidArg<A> extends true ? new (p1: A) => TReturn :
new () => TReturn :
never;
type ReplaceCtorReturn<T, TReturn> =
T extends new (a: infer A, b: infer B) => void ?
IsValidArg<B> extends true ? new (p1: A, p2: B) => TReturn :
IsValidArg<A> extends true ? new (p1: A) => TReturn :
new () => TReturn :
never;
type ConstructorOrDefault<T> = T extends { constructor: infer TCtor } ? TCtor : () => void;
function Class(name: string): {
extends<TBase extends {
new(...args: any[]): any,
__original: any
}, T>(base: TBase, members: (b: { Super: (t: any) => TBase['__original'] }) => T & ThisType<T & InstanceType<TBase>>):
T extends { constructor: infer TCtor } ?
FunctionToConstructor<ConstructorOrDefault<T>, InstanceType<TBase> & Pick<T, Exclude<keyof T, 'constructor'>>>
:
ReplaceCtorReturn<TBase, InstanceType<TBase> & Pick<T, Exclude<keyof T, 'constructor'>>>
}
function Class<T>(name: string, members: T & ThisType<T>): FunctionToConstructor<ConstructorOrDefault<T>, Pick<T, Exclude<keyof T, 'constructor'>>> & { __original: T }
function Class(): any {
return null as any;
}
const Animal = Class('Animal', {
sound: '',
constructor(sound: string) {
this.sound = sound;
},
makeSound() { console.log(this.sound) }
})
new Animal('').makeSound();
const Dog = Class('Dog').extends(Animal, ({ Super }) => ({
constructor(size: 'small' | 'big') {
if (size === 'small')
Super(this).constructor('woof')
if (size === 'big')
Super(this).constructor('WOOF')
},
makeSound(d: number) { console.log(this.sound) },
bark() { this.makeSound() },
other() {
this.bark();
}
}))
type Dog = InstanceType<typeof Dog>
const smallDog: Dog = new Dog('small')
smallDog.bark() // "woof"
const bigDog = new Dog('big')
bigDog.bark() // "WOOF"
bigDog.bark();
bigDog.makeSound();
希望这有帮助,如果我能提供更多帮助,请告诉我 :)
关于javascript - 我们如何键入一个类工厂来生成一个给定对象字面量的类?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50899400/
我的一位教授给了我们一些考试练习题,其中一个问题类似于下面(伪代码): a.setColor(blue); b.setColor(red); a = b; b.setColor(purple); b
我似乎经常使用这个测试 if( object && object !== "null" && object !== "undefined" ){ doSomething(); } 在对象上,我
C# Object/object 是值类型还是引用类型? 我检查过它们可以保留引用,但是这个引用不能用于更改对象。 using System; class MyClass { public s
我在通过 AJAX 发送 json 时遇到问题。 var data = [{"name": "Will", "surname": "Smith", "age": "40"},{"name": "Wil
当我尝试访问我的 View 中的对象 {{result}} 时(我从 Express js 服务器发送该对象),它只显示 [object][object]有谁知道如何获取 JSON 格式的值吗? 这是
我有不同类型的数据(可能是字符串、整数......)。这是一个简单的例子: public static void main(String[] args) { before("one"); }
嗨,我是 json 和 javascript 的新手。 我在这个网站找到了使用json数据作为表格的方法。 我很好奇为什么当我尝试使用 json 数据作为表时,我得到 [Object,Object]
已关闭。此问题需要 debugging details 。目前不接受答案。 编辑问题以包含 desired behavior, a specific problem or error, and the
我听别人说 null == object 比 object == null check 例如: void m1(Object obj ) { if(null == obj) // Is thi
Match 对象 提供了对正则表达式匹配的只读属性的访问。 说明 Match 对象只能通过 RegExp 对象的 Execute 方法来创建,该方法实际上返回了 Match 对象的集合。所有的
Class 对象 使用 Class 语句创建的对象。提供了对类的各种事件的访问。 说明 不允许显式地将一个变量声明为 Class 类型。在 VBScript 的上下文中,“类对象”一词指的是用
Folder 对象 提供对文件夹所有属性的访问。 说明 以下代码举例说明如何获得 Folder 对象并查看它的属性: Function ShowDateCreated(f
File 对象 提供对文件的所有属性的访问。 说明 以下代码举例说明如何获得一个 File 对象并查看它的属性: Function ShowDateCreated(fil
Drive 对象 提供对磁盘驱动器或网络共享的属性的访问。 说明 以下代码举例说明如何使用 Drive 对象访问驱动器的属性: Function ShowFreeSpac
FileSystemObject 对象 提供对计算机文件系统的访问。 说明 以下代码举例说明如何使用 FileSystemObject 对象返回一个 TextStream 对象,此对象可以被读
我是 javascript OOP 的新手,我认为这是一个相对基本的问题,但我无法通过搜索网络找到任何帮助。我是否遗漏了什么,或者我只是以错误的方式解决了这个问题? 这是我的示例代码: functio
我可以很容易地创造出很多不同的对象。例如像这样: var myObject = { myFunction: function () { return ""; } };
function Person(fname, lname) { this.fname = fname, this.lname = lname, this.getName = function()
任何人都可以向我解释为什么下面的代码给出 (object, Object) 吗? (console.log(dope) 给出了它应该的内容,但在 JSON.stringify 和 JSON.parse
我正在尝试完成散点图 exercise来自免费代码营。然而,我现在只自己学习了 d3 几个小时,在遵循 lynda.com 的教程后,我一直在尝试确定如何在工具提示中显示特定数据。 This code
我是一名优秀的程序员,十分优秀!