mirror of
https://github.com/zhigang1992/DefinitelyTyped.git
synced 2026-01-13 09:00:42 +08:00
[core-object] Add typings for core-object (#29344)
This commit is contained in:
committed by
Wesley Wigham
parent
b4529f27c1
commit
52fe47713e
40
types/core-object/-private/utils.d.ts
vendored
Normal file
40
types/core-object/-private/utils.d.ts
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
export type Constructor<Instance> = new (...args: any[]) => Instance;
|
||||
export type Mix<T, U> = U & Pick<T, Exclude<keyof T, keyof U>>;
|
||||
export type Values<T> = T[keyof T];
|
||||
|
||||
/** Just the strings corresponding to method names on the given type */
|
||||
export type MethodNames<T> = Values<{ [K in keyof T]: T[K] extends (...args: any[]) => any ? K : never }>;
|
||||
|
||||
/**
|
||||
* Given a `this` type, arg types tuple and return type, creates a function-ish
|
||||
* type that can only be only be invoked via `.call` or `.apply`. Because of the
|
||||
* way CoreObject's `_super` works, it's an error to write e.g. `this._super.init()`;
|
||||
*/
|
||||
export interface CallOrApply<This, Args, Return> {
|
||||
apply: (thisArg: This, args: Args extends undefined ? any[] : Args | IArguments) => Return;
|
||||
|
||||
// TODO support this properly with `...args: Args` once we can restrict to 3.0+ on DT
|
||||
call:
|
||||
Args extends undefined ? (thisArg: This) => Return :
|
||||
Args extends [infer A] ? (thisArg: This, a: A) => Return :
|
||||
Args extends [infer A, infer B] ? (thisArg: This, a: A, b: B) => Return :
|
||||
Args extends [infer A, infer B, infer C] ? (thisArg: This, a: A, b: B, c: C) => Return :
|
||||
Args extends [infer A, infer B, infer C, infer D] ? (thisArg: This, a: A, b: B, c: C, d: D) => Return :
|
||||
(thisArg: This, ...args: any[]) => Return;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of `this._super`, which has keys for all methods appearing in the given
|
||||
* type, but forces the caller to use `.call` or `.apply` to invoke them.
|
||||
*/
|
||||
export type Super<T> = {
|
||||
// TODO just do `infer Args` once we can restrict to 3.0+ on DT
|
||||
[K in MethodNames<T>]:
|
||||
T[K] extends () => infer Return ? CallOrApply<T, undefined, Return> :
|
||||
T[K] extends (a: infer A) => infer Return ? CallOrApply<T, [A], Return> :
|
||||
T[K] extends (a: infer A, b: infer B) => infer Return ? CallOrApply<T, [A, B], Return> :
|
||||
T[K] extends (a: infer A, b: infer B, c: infer C) => infer Return ? CallOrApply<T, [A, B, C], Return> :
|
||||
T[K] extends (a: infer A, b: infer B, c: infer C, d: infer D) => infer Return ? CallOrApply<T, [A, B, C, D], Return> :
|
||||
T[K] extends (...args: any[]) => infer Return ? CallOrApply<T, any[], Return> :
|
||||
never;
|
||||
};
|
||||
163
types/core-object/core-object-tests.ts
Normal file
163
types/core-object/core-object-tests.ts
Normal file
@@ -0,0 +1,163 @@
|
||||
import { Mix, MethodNames } from 'core-object/-private/utils';
|
||||
import CoreObject, { ExtendOptions, ExtendThisType } from 'core-object';
|
||||
|
||||
//////////// Mix ////////////
|
||||
|
||||
declare const mix1: Mix<{ a: number }, { b: string }>;
|
||||
mix1.a; // $ExpectType number
|
||||
mix1.b; // $ExpectType string
|
||||
|
||||
declare const mix2: Mix<{ a: number }, { a: string }>;
|
||||
mix2.a; // $ExpectType string
|
||||
mix2.b; // $ExpectError
|
||||
|
||||
//////////// MethodNames ////////////
|
||||
|
||||
declare const names1: MethodNames<{ a: string }>;
|
||||
names1; // $ExpectType never
|
||||
|
||||
declare const names2: MethodNames<{ a: () => number; b(arg: number): void }>;
|
||||
names2; // $ExpectType "a" | "b"
|
||||
|
||||
declare const names3: MethodNames<{ a: () => 'hi'; b: null }>;
|
||||
names3; // $ExpectType "a"
|
||||
|
||||
//////////// ExtendOptions ////////////
|
||||
|
||||
const extendOptions1: ExtendOptions<{}> = { a: 1, b: 'hi' };
|
||||
const extendOptions2: ExtendOptions<{ a: number }> = { b: 'hi' };
|
||||
const extendOptions3: ExtendOptions<{ a: number }> = { a: 5 };
|
||||
const extendOptions4: ExtendOptions<{ a: number }> = { a: 'hi' }; // $ExpectError
|
||||
|
||||
//////////// ExtendThisType ////////////
|
||||
|
||||
declare function extendThisType1<T>(options: T & ExtendThisType<{ prop: string; method: () => number }, T>): void;
|
||||
extendThisType1({
|
||||
otherMethod() {
|
||||
this.prop; // $ExpectType string
|
||||
this.random; // $ExpectError
|
||||
|
||||
this._super.method.call(this); // $ExpectType number
|
||||
this._super.random; // $ExpectError
|
||||
}
|
||||
});
|
||||
|
||||
//////////// CoreObject ////////////
|
||||
|
||||
const A = CoreObject.extend({
|
||||
foo: 'hello',
|
||||
|
||||
method(): string {
|
||||
return this.foo;
|
||||
}
|
||||
});
|
||||
|
||||
const a = new A();
|
||||
a.foo; // $ExpectType string
|
||||
a.bar; // $ExpectError
|
||||
a.method(); // $ExpectType string
|
||||
|
||||
const B = A.extend({
|
||||
bar: 123,
|
||||
|
||||
other(): string {
|
||||
return this._super.method.call(this) + this.foo;
|
||||
}
|
||||
});
|
||||
|
||||
const b = new B();
|
||||
b.foo; // $ExpectType string
|
||||
b.bar; // $ExpectType number
|
||||
b.method(); // $ExpectType string
|
||||
b.other(); // $ExpectType string
|
||||
|
||||
class ClassWithMethods extends CoreObject.extend({
|
||||
extendMethod(arg: number): string {
|
||||
return 'ok';
|
||||
}
|
||||
}) {
|
||||
esMethod(arg: string): number {
|
||||
return 123;
|
||||
}
|
||||
}
|
||||
|
||||
const ExtendSubclass = ClassWithMethods.extend({
|
||||
anotherMethod() {
|
||||
this._super.extendMethod(1); // $ExpectError
|
||||
this._super.esMethod('hi'); // $ExpectError
|
||||
|
||||
this._super.extendMethod.call(this, 1); // $ExpectType string
|
||||
this._super.extendMethod.apply(this, [1]); // $ExpectType string
|
||||
|
||||
this._super.esMethod.call(this, 'hi'); // $ExpectType number
|
||||
this._super.esMethod.apply(this, ['hi']); // $ExpectType number
|
||||
}
|
||||
});
|
||||
|
||||
class ESSubclass extends ClassWithMethods {
|
||||
anotherMethod() {
|
||||
this._super; // $ExpectError
|
||||
|
||||
super.extendMethod(1); // $ExpectType string
|
||||
super.esMethod('hi'); // $ExpectType number
|
||||
}
|
||||
}
|
||||
|
||||
ClassWithMethods.extend({ extendMethod: null }); // $ExpectError
|
||||
ClassWithMethods.extend({ extendMethod() {} }); // $ExpectError
|
||||
ClassWithMethods.extend({ esMethod: null }); // $ExpectError
|
||||
ClassWithMethods.extend({ esMethod() {} }); // $ExpectError
|
||||
|
||||
ClassWithMethods.extend({
|
||||
extendMethod(arg: number): string {
|
||||
const result = this._super.extendMethod.call(this, arg);
|
||||
result; // $ExpectType string
|
||||
return result;
|
||||
},
|
||||
|
||||
esMethod(arg: string): number {
|
||||
const result = this._super.esMethod.call(this, arg);
|
||||
result; // $ExpectType number
|
||||
return result;
|
||||
}
|
||||
});
|
||||
|
||||
declare class ClassWithManyMethods extends CoreObject {
|
||||
method0(): 0;
|
||||
method1(a: 'a'): 1;
|
||||
method2(a: 'a', b: 'b'): 2;
|
||||
method3(a: 'a', b: 'b', c: 'c'): 3;
|
||||
method4(a: 'a', b: 'b', c: 'c', d: 'd'): 4;
|
||||
method5(a: 'a', b: 'b', c: 'c', d: 'd', e: 'e'): 5;
|
||||
}
|
||||
|
||||
ClassWithManyMethods.extend({
|
||||
child(): void {
|
||||
this._super.method0.call(this); // $ExpectType 0
|
||||
this._super.method0.apply(this, []); // $ExpectType 0
|
||||
|
||||
this._super.method1.call(this, 'a'); // $ExpectType 1
|
||||
this._super.method1.apply(this, ['a']); // $ExpectType 1
|
||||
this._super.method1.call(this, 'x'); // $ExpectError
|
||||
this._super.method1.apply(this, ['x']); // $ExpectError
|
||||
|
||||
this._super.method2.call(this, 'a', 'b'); // $ExpectType 2
|
||||
this._super.method2.apply(this, ['a', 'b']); // $ExpectType 2
|
||||
this._super.method2.call(this, 'a', 'x'); // $ExpectError
|
||||
this._super.method2.apply(this, ['a', 'x']); // $ExpectError
|
||||
|
||||
this._super.method3.call(this, 'a', 'b', 'c'); // $ExpectType 3
|
||||
this._super.method3.apply(this, ['a', 'b', 'c']); // $ExpectType 3
|
||||
this._super.method3.call(this, 'a', 'b', 'x'); // $ExpectError
|
||||
this._super.method3.apply(this, ['a', 'b', 'x']); // $ExpectError
|
||||
|
||||
this._super.method4.call(this, 'a', 'b', 'c', 'd'); // $ExpectType 4
|
||||
this._super.method4.apply(this, ['a', 'b', 'c', 'd']); // $ExpectType 4
|
||||
this._super.method4.call(this, 'a', 'b', 'c', 'x'); // $ExpectError
|
||||
this._super.method4.apply(this, ['a', 'b', 'c', 'x']); // $ExpectError
|
||||
|
||||
// Arity 4 is as high as we go for arg checking
|
||||
this._super.method5.call(this, 'foo', 'bar', 'baz'); // $ExpectType 5
|
||||
this._super.method5.apply(this, ['foo', 'bar', 'baz']); // $ExpectType 5
|
||||
}
|
||||
});
|
||||
23
types/core-object/index.d.ts
vendored
Normal file
23
types/core-object/index.d.ts
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
// Type definitions for core-object 3.0
|
||||
// Project: https://github.com/ember-cli/core-object
|
||||
// Definitions by: Dan Freeman <https://github.com/dfreeman>
|
||||
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
||||
// TypeScript Version: 2.8
|
||||
|
||||
import { Mix, Super, Constructor } from './-private/utils';
|
||||
|
||||
/** The type of options allowed to be passed to `Base.extend()` */
|
||||
export type ExtendOptions<Base> = { [K in keyof Base]?: Base[K] } & Record<string, any>;
|
||||
|
||||
/** The `this` type for any methods on the options passed to `Base.extend()` */
|
||||
export type ExtendThisType<Base, Ext> = ThisType<Mix<Base, Ext> & { _super: Super<Base> }>;
|
||||
|
||||
export default class CoreObject {
|
||||
// TODO restrict to `Record<string, unknown>` once we can restrict to 3.0+ on DT
|
||||
init(options?: Record<string, any>): void;
|
||||
|
||||
static extend<BaseClass extends Constructor<any>, Ext extends ExtendOptions<InstanceType<BaseClass>>>(
|
||||
this: BaseClass,
|
||||
options: Ext & ExtendThisType<InstanceType<BaseClass>, Ext>
|
||||
): BaseClass & Constructor<Mix<InstanceType<BaseClass>, Ext>>;
|
||||
}
|
||||
24
types/core-object/tsconfig.json
Normal file
24
types/core-object/tsconfig.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"lib": [
|
||||
"es6"
|
||||
],
|
||||
"noImplicitAny": true,
|
||||
"noImplicitThis": true,
|
||||
"strictNullChecks": true,
|
||||
"strictFunctionTypes": true,
|
||||
"baseUrl": "../",
|
||||
"typeRoots": [
|
||||
"../"
|
||||
],
|
||||
"types": [],
|
||||
"noEmit": true,
|
||||
"forceConsistentCasingInFileNames": true
|
||||
},
|
||||
"files": [
|
||||
"index.d.ts",
|
||||
"-private/utils.d.ts",
|
||||
"core-object-tests.ts"
|
||||
]
|
||||
}
|
||||
1
types/core-object/tslint.json
Normal file
1
types/core-object/tslint.json
Normal file
@@ -0,0 +1 @@
|
||||
{ "extends": "dtslint/dt.json" }
|
||||
Reference in New Issue
Block a user