Skip to content

Commit

Permalink
Merge pull request #19947 from wagenet/more-type-tests
Browse files Browse the repository at this point in the history
More type tests
  • Loading branch information
chriskrycho authored Feb 18, 2022
2 parents 8455848 + ff51eb4 commit 1cc378e
Show file tree
Hide file tree
Showing 72 changed files with 2,016 additions and 160 deletions.
2 changes: 2 additions & 0 deletions ember-cli-build.js
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,8 @@ function templateCompilerBundle(emberPackages, transpileTree) {
'@ember/-internals/*/tests/**' /* internal packages */,
'*/*/tests/**' /* scoped packages */,
'*/tests/**' /* packages */,
'*/*/type-tests/**' /* scoped packages */,
'*/type-tests/**' /* packages */,
],
}),
templateCompilerDependencies(),
Expand Down
16 changes: 7 additions & 9 deletions packages/@ember/-internals/glimmer/lib/helpers/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ function makeArgsProcessor(valuePathRef: Reference | false, actionArgsRef: Refer
function makeDynamicClosureAction(
context: object,
targetRef: Reference<MaybeActionHandler>,
actionRef: Reference<string | Function>,
actionRef: Reference<string | ((...args: any[]) => any)>,
processArgs: (args: unknown[]) => unknown[],
debugKey: string
) {
Expand All @@ -389,34 +389,32 @@ function makeDynamicClosureAction(
}

interface MaybeActionHandler {
actions?: Record<string, Function>;
actions?: Record<string, (...args: any[]) => any>;
}

function makeClosureAction(
context: object,
target: MaybeActionHandler,
action: string | Function,
action: string | ((...args: any[]) => any),
processArgs: (args: unknown[]) => unknown[],
debugKey: string
) {
let self: object;
let fn: Function;
let fn: (...args: any[]) => any;

assert(
`Action passed is null or undefined in (action) from ${target}.`,
action !== undefined && action !== null
);

let typeofAction = typeof action;

if (typeofAction === 'string') {
if (typeof action === 'string') {
self = target;
fn = (target.actions && target.actions[action as string])!;

assert(`An action named '${action}' was not found in ${target}`, Boolean(fn));
} else if (typeofAction === 'function') {
} else if (typeof action === 'function') {
self = context;
fn = action as Function;
fn = action;
} else {
assert(
`An action could not be made for \`${
Expand Down
7 changes: 5 additions & 2 deletions packages/@ember/-internals/glimmer/lib/views/outlet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { createComputeRef, Reference, updateRef } from '@glimmer/reference';
import { consumeTag, createTag, dirtyTag } from '@glimmer/validator';
import { SimpleElement } from '@simple-dom/interface';
import { OutletDefinitionState } from '../component-managers/outlet';
import { Renderer } from '../renderer';
import { OutletState } from '../utils/outlet';

export interface BootEnvironment {
Expand Down Expand Up @@ -95,9 +96,11 @@ export default class OutletView {
target = selector;
}

let renderer = this.owner.lookup('renderer:-dom');
let renderer = this.owner.lookup('renderer:-dom') as Renderer;

schedule('render', renderer, 'appendOutletView', this, target);
// SAFETY: It's not clear that this cast is safe.
// The types for appendOutletView may be incorrect or this is a potential bug.
schedule('render', renderer, 'appendOutletView', this, target as SimpleElement);
}

rerender(): void {
Expand Down
3 changes: 1 addition & 2 deletions packages/@ember/-internals/metal/lib/is_blank.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import isEmpty from './is_empty';
```javascript
import { isBlank } from '@ember/utils';
isBlank(); // true
isBlank(null); // true
isBlank(undefined); // true
isBlank(''); // true
Expand All @@ -29,6 +28,6 @@ import isEmpty from './is_empty';
@since 1.5.0
@public
*/
export default function isBlank(obj: any): boolean {
export default function isBlank(obj: unknown): boolean {
return isEmpty(obj) || (typeof obj === 'string' && /\S/.test(obj) === false);
}
34 changes: 20 additions & 14 deletions packages/@ember/-internals/metal/lib/is_empty.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { get } from './property_get';
import { get, MaybeHasUnknownProperty } from './property_get';
/**
@module @ember/utils
*/
Expand All @@ -13,7 +13,6 @@ import { get } from './property_get';
to check emptiness.
```javascript
isEmpty(); // true
isEmpty(null); // true
isEmpty(undefined); // true
isEmpty(''); // true
Expand All @@ -35,33 +34,40 @@ import { get } from './property_get';
@return {Boolean}
@public
*/
export default function isEmpty(obj: any): boolean {
let none = obj === null || obj === undefined;
if (none) {
return none;
export default function isEmpty(obj: unknown): boolean {
if (obj === null || obj === undefined) {
return true;
}

if (typeof obj.unknownProperty !== 'function' && typeof obj.size === 'number') {
return !obj.size;
if (
typeof (obj as MaybeHasUnknownProperty).unknownProperty !== 'function' &&
typeof (obj as HasSize).size === 'number'
) {
return !(obj as HasSize).size;
}

let objectType = typeof obj;

if (objectType === 'object') {
if (typeof obj === 'object') {
let size = get(obj, 'size');
if (typeof size === 'number') {
return !size;
}

let length = get(obj, 'length');
if (typeof length === 'number') {
return !length;
}
}

if (typeof obj.length === 'number' && objectType !== 'function') {
return !obj.length;
if (typeof (obj as HasLength).length === 'number' && typeof obj !== 'function') {
return !(obj as HasLength).length;
}

return false;
}

interface HasSize {
size: number;
}

interface HasLength {
length: number;
}
1 change: 0 additions & 1 deletion packages/@ember/-internals/metal/lib/is_none.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
confusing.
```javascript
isNone(); // true
isNone(null); // true
isNone(undefined); // true
isNone(''); // false
Expand Down
3 changes: 1 addition & 2 deletions packages/@ember/-internals/metal/lib/is_present.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import isBlank from './is_blank';
A value is present if it not `isBlank`.
```javascript
isPresent(); // false
isPresent(null); // false
isPresent(undefined); // false
isPresent(''); // false
Expand All @@ -32,6 +31,6 @@ import isBlank from './is_blank';
@since 1.8.0
@public
*/
export default function isPresent(obj: object): boolean {
export default function isPresent<T>(obj: T | null | undefined): obj is T {
return !isBlank(obj);
}
2 changes: 1 addition & 1 deletion packages/@ember/-internals/metal/lib/property_get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ if (DEBUG) {
};
}

interface MaybeHasUnknownProperty {
export interface MaybeHasUnknownProperty {
unknownProperty?: (keyName: string) => any;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ export default class HashLocation extends EmberObject implements EmberLocation {
*/
onUpdateURL(callback: UpdateCallback): void {
this._removeEventListener();
this._hashchangeHandler = bind(this, function (this: HashLocation) {
this._hashchangeHandler = bind(this, function (this: HashLocation, _event: Event) {
let path = this.getURL();
if (this.lastSetURL === path) {
return;
Expand Down
15 changes: 9 additions & 6 deletions packages/@ember/-internals/routing/lib/system/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import Router, {
TransitionError,
TransitionState,
} from 'router_js';
import type { Timer } from 'backburner';
import { EngineRouteInfo } from './engines';
import EngineInstance from '@ember/engine/instance';

Expand Down Expand Up @@ -199,7 +200,7 @@ class EmberRouter<R extends Route = Route> extends EmberObject.extend(Evented) i
_engineInfoByRoute = Object.create(null);
_routerService: RouterService<R>;

_slowTransitionTimer: unknown;
_slowTransitionTimer: Timer | null = null;

private namespace: any;

Expand Down Expand Up @@ -810,10 +811,12 @@ class EmberRouter<R extends Route = Route> extends EmberObject.extend(Evented) i

let instances = this._engineInstances;
for (let name in instances) {
let instance = instances[name];
assert('has instance', instance);
for (let id in instance) {
run(instance[id], 'destroy');
let instanceMap = instances[name];
assert('has instanceMap', instanceMap);
for (let id in instanceMap) {
let instance = instanceMap[id];
assert('has instance', instance);
run(instance, 'destroy');
}
}
}
Expand Down Expand Up @@ -1334,7 +1337,7 @@ class EmberRouter<R extends Route = Route> extends EmberObject.extend(Evented) i
this._slowTransitionTimer = scheduleOnce(
'routerTransitions',
this,
'_handleSlowTransition',
this._handleSlowTransition,
transition,
originRoute
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { typeOf } from './type-of';
import Comparable from './mixins/comparable';
import { assert } from '@ember/debug';

const TYPE_ORDER = {
undefined: 0,
Expand All @@ -15,6 +16,8 @@ const TYPE_ORDER = {
date: 10,
};

type Compare = -1 | 0 | 1;

//
// the spaceship operator
//
Expand All @@ -32,9 +35,10 @@ const TYPE_ORDER = {
// | `._ `. \
// `._________`-. `. `.___
// SSt `------'`
function spaceship(a, b) {
function spaceship(a: number, b: number): Compare {
let diff = a - b;
return (diff > 0) - (diff < 0);
// SAFETY: Number casts true into 1 and false into 0. Therefore, this must end up as one of the Compare values.
return (Number(diff > 0) - Number(diff < 0)) as Compare;
}

/**
Expand Down Expand Up @@ -87,20 +91,21 @@ function spaceship(a, b) {
@return {Number} -1 if v < w, 0 if v = w and 1 if v > w.
@public
*/
export default function compare(v, w) {
export default function compare(v: unknown, w: unknown): Compare {
if (v === w) {
return 0;
}

let type1 = typeOf(v);
let type2 = typeOf(w);

if (type1 === 'instance' && Comparable.detect(v) && v.constructor.compare) {
if (type1 === 'instance' && isComparable(v) && v.constructor.compare) {
return v.constructor.compare(v, w);
}

if (type2 === 'instance' && Comparable.detect(w) && w.constructor.compare) {
return w.constructor.compare(w, v) * -1;
if (type2 === 'instance' && isComparable(w) && w.constructor.compare) {
// SAFETY: Multiplying by a negative just changes the sign
return (w.constructor.compare(w, v) * -1) as Compare;
}

let res = spaceship(TYPE_ORDER[type1], TYPE_ORDER[type2]);
Expand All @@ -112,13 +117,17 @@ export default function compare(v, w) {
// types are equal - so we have to check values now
switch (type1) {
case 'boolean':
assert('both are boolean', typeof v === 'boolean' && typeof w === 'boolean');
return spaceship(Number(v), Number(w));
case 'number':
assert('both are numbers', typeof v === 'number' && typeof w === 'number');
return spaceship(v, w);

case 'string':
assert('both are strings', typeof v === 'string' && typeof w === 'string');
return spaceship(v.localeCompare(w), 0);

case 'array': {
assert('both are arrays', Array.isArray(v) && Array.isArray(w));
let vLen = v.length;
let wLen = w.length;
let len = Math.min(vLen, wLen);
Expand All @@ -135,15 +144,24 @@ export default function compare(v, w) {
return spaceship(vLen, wLen);
}
case 'instance':
if (Comparable.detect(v)) {
if (isComparable(v) && v.compare) {
return v.compare(v, w);
}
return 0;

case 'date':
assert('both are dates', v instanceof Date && w instanceof Date);
return spaceship(v.getTime(), w.getTime());

default:
return 0;
}
}

interface ComparableConstructor {
constructor: Comparable;
}

function isComparable(value: unknown): value is Comparable & ComparableConstructor {
return Comparable.detect(value);
}
3 changes: 0 additions & 3 deletions packages/@ember/-internals/runtime/lib/copy.d.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@
@return {Boolean}
@public
*/
export default function isEqual(a, b) {
if (a && typeof a.isEqual === 'function') {
return a.isEqual(b);
export default function isEqual(a: unknown, b: unknown): boolean {
if (a && typeof (a as IsEqual).isEqual === 'function') {
return (a as IsEqual).isEqual(b);
}

if (a instanceof Date && b instanceof Date) {
Expand All @@ -58,3 +58,7 @@ export default function isEqual(a, b) {

return a === b;
}

interface IsEqual {
isEqual(val: unknown): boolean;
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import Mixin from '../../types/mixin';
import { Mixin } from '@ember/-internals/metal';

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

interface Comparable {
compare: ((a: unknown, b: unknown) => -1 | 0 | 1) | null;
}
declare const Comparable: Mixin;

export default Comparable;
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Container } from '@ember/-internals/container';
import { TypeOptions } from '@ember/-internals/container/lib/registry';
import { Mixin } from '@ember/-internals/metal';
import { Factory } from '@ember/-internals/owner';
import Mixin from '../../types/mixin';

interface ContainerProxy {
/** @internal */
Expand Down
Loading

0 comments on commit 1cc378e

Please sign in to comment.