Skip to content

Commit

Permalink
Add types for runtime mixins
Browse files Browse the repository at this point in the history
  • Loading branch information
wagenet committed Feb 18, 2022
1 parent 35b12d1 commit 9b0d2ff
Show file tree
Hide file tree
Showing 16 changed files with 117 additions and 27 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3144,7 +3144,7 @@ Clearly, `component-a` has subscribed to `some-other-component`'s `action`. Prev
* CollectionView context is now its content
* Various enhancements to bound helpers: adds multiple property support to bound helpers, adds bind-able options hash properties, adds {{unbound}} helper support to render unbound form of helpers.
* Add App.inject
* Add Ember.EnumberableUtils.intersection
* Add Ember.EnumerableUtils.intersection
* Deprecate Controller#controllerFor in favor of Controller#needs
* Adds `bubbles` property to Ember.TextField
* Allow overriding of Ember.Router#handleURL
Expand Down
15 changes: 5 additions & 10 deletions packages/@ember/-internals/container/lib/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -308,15 +308,10 @@ function factoryFor(
return manager;
}

interface FactoryOptions {
instantiate?: boolean;
singleton?: boolean;
}

function isSingletonClass(
container: Container,
fullName: string,
{ instantiate, singleton }: FactoryOptions
{ instantiate, singleton }: TypeOptions
) {
return (
singleton !== false &&
Expand All @@ -329,7 +324,7 @@ function isSingletonClass(
function isSingletonInstance(
container: Container,
fullName: string,
{ instantiate, singleton }: FactoryOptions
{ instantiate, singleton }: TypeOptions
) {
return (
singleton !== false &&
Expand All @@ -342,7 +337,7 @@ function isSingletonInstance(
function isFactoryClass(
container: Container,
fullname: string,
{ instantiate, singleton }: FactoryOptions
{ instantiate, singleton }: TypeOptions
) {
return (
instantiate === false &&
Expand All @@ -354,7 +349,7 @@ function isFactoryClass(
function isFactoryInstance(
container: Container,
fullName: string,
{ instantiate, singleton }: FactoryOptions
{ instantiate, singleton }: TypeOptions
) {
return (
instantiate !== false &&
Expand All @@ -367,7 +362,7 @@ function instantiateFactory(
container: Container,
normalizedName: string,
fullName: string,
options: FactoryOptions
options: TypeOptions
) {
let factoryManager = factoryFor(container, normalizedName, fullName);

Expand Down
14 changes: 11 additions & 3 deletions packages/@ember/-internals/container/lib/registry.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Factory } from '@ember/-internals/owner';
import { dictionary, intern } from '@ember/-internals/utils';
import { assert, deprecate } from '@ember/debug';
import { set } from '@ember/object';
import { DEBUG } from '@glimmer/env';
import Container, { ContainerOptions, LazyInjection } from './container';

export interface Injection {
property: string;
specifier: string;
Expand Down Expand Up @@ -46,6 +46,10 @@ export type NotResolver = {

export type Resolve = <T, C>(name: string) => Factory<T, C> | undefined;

export interface ResolverClass {
create(...args: unknown[]): Resolver;
}

export interface Resolver {
knownForType?: (type: string) => KnownForTypeResult;
lookupDescription?: (fullName: string) => string;
Expand Down Expand Up @@ -77,7 +81,7 @@ const VALID_FULL_NAME_REGEXP = /^[^:]+:[^:]+$/;
*/
export default class Registry implements IRegistry {
readonly _failSet: Set<string>;
resolver: Resolver | (Resolve & NotResolver) | null;
resolver: Resolver | null;
readonly fallback: IRegistry | null;
readonly registrations: Record<string, object>;
_localLookupCache: Record<string, object>;
Expand All @@ -86,6 +90,8 @@ export default class Registry implements IRegistry {
readonly _resolveCache: Record<string, object>;
readonly _typeOptions: Record<string, TypeOptions>;

set?: typeof set;

constructor(options: RegistryOptions = {}) {
this.fallback = options.fallback || null;
this.resolver = options.resolver || null;
Expand Down Expand Up @@ -182,7 +188,9 @@ export default class Registry implements IRegistry {
@param {Function} factory
@param {Object} options
*/
register(fullName: string, factory: Factory<unknown>, options: TypeOptions = {}): void {
register(fullName: string, factory: object, options: TypeOptions & { instantiate: false }): void;
register(fullName: string, factory: Factory<unknown>, options?: TypeOptions): void;
register(fullName: string, factory: object, options: TypeOptions = {}): void {
assert('fullName must be a proper full name', this.isValidFullName(fullName));
assert(`Attempting to register an unknown factory: '${fullName}'`, factory !== undefined);

Expand Down
3 changes: 2 additions & 1 deletion packages/@ember/-internals/metal/lib/property_get.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/**
@module @ember/object
*/
import ProxyMixin from '@ember/-internals/runtime/lib/mixins/-proxy';
import { isEmberArray, setProxy, symbol } from '@ember/-internals/utils';
import { assert } from '@ember/debug';
import { DEBUG } from '@glimmer/env';
Expand Down Expand Up @@ -159,7 +160,7 @@ _getProp({ unknownProperty() {} }, 1 as any);
get({}, 'foo');
get({}, 'foo.bar');

let fakeProxy = {};
let fakeProxy = {} as ProxyMixin<unknown>;
setProxy(fakeProxy);

track(() => _getProp({}, 'a'));
Expand Down
28 changes: 28 additions & 0 deletions packages/@ember/-internals/runtime/lib/mixins/-proxy.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Mixin } from '@ember/-internals/metal';

export function contentFor<T>(proxy: ProxyMixin<T>): T | null;

interface ProxyMixin<T = unknown> {
/**
The object whose properties will be forwarded.
@property content
@type {unknown}
@default null
@public
*/
content: T | null;

willDestroy(): void;

isTruthy: boolean;

unknownProperty<K extends keyof T>(key: K): T[K] | undefined;
unknownProperty(key: string): unknown;

setUnknownProperty<K extends keyof T>(key: K, value: T[K]): T[K];
setUnknownProperty<V>(key: string, value: V): V;
}
declare const ProxyMixin: Mixin;

export default ProxyMixin;
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import { Mixin } from '@ember/-internals/metal';

export default class ActionHandler extends Mixin {}
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface ActionHandler {}
declare const ActionHandler: Mixin;

export default ActionHandler;
6 changes: 4 additions & 2 deletions packages/@ember/-internals/runtime/lib/mixins/array.d.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { Mixin } from '@ember/-internals/metal';
import Enumerable from './enumerable';
import MutableEnumerable from './mutable_enumerable';

type Value<T, K extends string> = K extends keyof T ? T[K] : unknown;

interface EmberArray<T> {
interface EmberArray<T> extends Enumerable {
length: number;
objectAt(idx: number): T | undefined;
objectsAt(indexes: number[]): Array<T | undefined>;
Expand Down Expand Up @@ -67,7 +69,7 @@ interface EmberArray<T> {
declare const EmberArray: Mixin;
export default EmberArray;

interface MutableArray<T> extends EmberArray<T> {
interface MutableArray<T> extends EmberArray<T>, MutableEnumerable {
replace(idx: number, amt: number, objects: T[]): void;
clear(): this;
insertAt(idx: number, object: T): this;
Expand Down
2 changes: 1 addition & 1 deletion packages/@ember/-internals/runtime/lib/mixins/array.js
Original file line number Diff line number Diff line change
Expand Up @@ -1561,7 +1561,7 @@ const MutableArray = Mixin.create(ArrayMixin, MutableEnumerable, {
```
@method unshiftObjects
@param {Enumberable} objects the objects to add
@param {Enumerable} objects the objects to add
@return {EmberArray} receiver
@public
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import { TypeOptions } from '@ember/-internals/container/lib/registry';
import { Mixin } from '@ember/-internals/metal';
import { Factory } from '@ember/-internals/owner';

interface ContainerProxy {
interface ContainerProxyMixin {
/** @internal */
__container__: Container;

ownerInjection(): void;
lookup(fullName: string, options?: TypeOptions): unknown;
factoryFor(fullName: string): Factory<unknown> | undefined;
}
declare const ContainerProxy: Mixin;
declare const ContainerProxyMixin: Mixin;

export default ContainerProxy;
export default ContainerProxyMixin;
7 changes: 7 additions & 0 deletions packages/@ember/-internals/runtime/lib/mixins/enumerable.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Mixin } from '@ember/-internals/metal';

// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface Enumerable {}
declare const Enumerable: Mixin;

export default Enumerable;
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Mixin } from '@ember/-internals/metal';
import Enumerable from './enumerable';

// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface MutableEnumerable extends Enumerable {}
declare const MutableEnumerable: Mixin;

export default MutableEnumerable;
24 changes: 24 additions & 0 deletions packages/@ember/-internals/runtime/lib/mixins/promise_proxy.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Mixin } from '@ember/-internals/metal';

interface PromiseProxyMixin<T> {
reason: null;

readonly isPending: boolean;

readonly isSettled: boolean;

isRejected: boolean;

isFulfilled: boolean;

promise: Promise<T>;

then: this['promise']['then'];

catch: this['promise']['catch'];

finally: this['promise']['finally'];
}
declare const PromiseProxyMixin: Mixin;

export default PromiseProxyMixin;
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export default class CoreObject {
*/
isDestroying: boolean;

// NOTE That the actual CoreObject#destroy returns `this`;
/**
* Destroys an object by setting the `isDestroyed` flag and removing its
* metadata, which effectively destroys observers and bindings.
Expand All @@ -72,7 +73,7 @@ export default class CoreObject {
* happen immediately. It will set an isDestroying flag immediately.
* @return receiver
*/
destroy(): CoreObject;
destroy(): void;

/**
* Override to implement teardown.
Expand Down Expand Up @@ -118,4 +119,7 @@ export default class CoreObject {

/** @internal */
static isMethod: boolean;

/** @internal */
static superclass: unknown;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import FrameworkObject from './object';
import _ProxyMixin from '../mixins/-proxy';

// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface ObjectProxy extends _ProxyMixin {}
declare class ObjectProxy extends FrameworkObject {}

export default ObjectProxy;
5 changes: 3 additions & 2 deletions packages/@ember/-internals/utils/lib/is_proxy.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import ProxyMixin from '@ember/-internals/runtime/lib/mixins/-proxy';
import { _WeakSet as WeakSet } from '@glimmer/util';
import { isObject } from './spec';

const PROXIES = new WeakSet();

export function isProxy(value: any | undefined | null): value is object {
export function isProxy(value: unknown): value is ProxyMixin<unknown> {
if (isObject(value)) {
return PROXIES.has(value);
}
return false;
}

export function setProxy(object: object): void {
export function setProxy(object: ProxyMixin<unknown>): void {
if (isObject(object)) {
PROXIES.add(object);
}
Expand Down
4 changes: 2 additions & 2 deletions packages/@ember/object/type-tests/core/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ const co1 = new CoreObject(owner);
expectTypeOf(co1.concatenatedProperties).toEqualTypeOf<string[]>();
expectTypeOf(co1.isDestroyed).toEqualTypeOf<boolean>();
expectTypeOf(co1.isDestroying).toEqualTypeOf<boolean>();
expectTypeOf(co1.destroy()).toEqualTypeOf<CoreObject>();
expectTypeOf(co1.destroy()).toEqualTypeOf<void>();
expectTypeOf(co1.toString()).toEqualTypeOf<string>();

/** .create tests */
const co2 = CoreObject.create();
expectTypeOf(co2.concatenatedProperties).toEqualTypeOf<string[]>();
expectTypeOf(co2.isDestroyed).toEqualTypeOf<boolean>();
expectTypeOf(co2.isDestroying).toEqualTypeOf<boolean>();
expectTypeOf(co2.destroy()).toEqualTypeOf<CoreObject>();
expectTypeOf(co2.destroy()).toEqualTypeOf<void>();
expectTypeOf(co2.toString()).toEqualTypeOf<string>();

/** .create tests w/ initial instance data passed in */
Expand Down

0 comments on commit 9b0d2ff

Please sign in to comment.