Fix a number of gaps in Ember's types (#38690)

* @ember/polyfills: support `undefined`.

Fixes #38681.

* @ember/utils: support JS objects in `typeOf`.

- Fixes #36809.
- Fixes typed-ember/ember-cli-typescript#799

* @ember/debug: add `deprecate` type and tests

Fixes #38682.
This commit is contained in:
Chris Krycho 2019-10-03 16:29:51 -06:00 committed by Ryan Cavanaugh
parent 3972699da2
commit 70b7904bb1
7 changed files with 117 additions and 59 deletions

View File

@ -1,4 +1,12 @@
import { runInDebug, warn, debug, assert, registerWarnHandler, registerDeprecationHandler } from "@ember/debug";
import {
runInDebug,
warn,
debug,
assert,
registerWarnHandler,
registerDeprecationHandler,
deprecate,
} from '@ember/debug';
/**
* @ember/debug tests
@ -43,3 +51,14 @@ registerDeprecationHandler((message, { id, until }, next) => { // $ExpectType vo
until; // $ExpectType string
next; // $ExpectType () => void
});
deprecate(); // $ExpectError
deprecate('missing test and options'); // $ExpectError
deprecate('missing options', true); // $ExpectError
deprecate('missing options', false); // $ExpectError
deprecate('missing options body', true, {}); // $ExpectError
deprecate('missing options id', true, { until: 'v4.0.0' }); // $ExpectError
deprecate('missing options until', true, { id: 'some.deprecation' }); // $ExpectError
deprecate('a valid deprecation without url', true, { id: 'some.deprecation', until: 'v4.0.0' }); // $ExpectType void
deprecate('incorrect options url', true, { id: 'some.deprecation', until: 'v4.0.0', url: 123 }); // $ExpectError
deprecate('a valid deprecation with url', true, { id: 'some.deprecation', until: 'v4.0.0', url: 'https://example.com/ember-deprecations-yo' }); // $ExpectType void

View File

@ -51,3 +51,36 @@ export function warn(message: string, test: boolean, options?: { id?: string }):
* @deprecated Missing deprecation options: https://emberjs.com/deprecations/v2.x/#toc_ember-debug-function-options
*/
export function warn(message: string, options?: { id?: string }): void;
/**
* Display a deprecation warning with the provided message and a stack trace
* (Chrome and Firefox only).
*
* In a production build, this method is defined as an empty function (NOP).
* Uses of this method in Ember itself are stripped from the ember.prod.js build.
*
* @param message A description of the deprecation.
* @param test If falsy, the deprecation will be displayed.
* @param options The deprecation options.
*/
export function deprecate(
message: string,
test: boolean,
options: {
/**
* A unique id for this deprecation. The id can be used by Ember debugging
* tools to change the behavior (raise, log or silence) for that specific
* deprecation. The id should be namespaced by dots, e.g.
* `"view.helper.select"`.
*/
id: string;
/**
* The version of Ember when this deprecation warning will be removed.
*/
until: string;
/**
* An optional url to the transition guide on the emberjs.com website.
*/
url?: string;
},
): void;

View File

@ -1,9 +1,10 @@
import { assign, merge } from '@ember/polyfills';
(() => { /* assign */
assign({}, { a: 'b'});
assign({}, { a: 'b'}).a; // $ExpectType string
assign({ a: 6 }, { a: 'b'}).a; // $ExpectType string
(() => {
/* assign */
assign({}, { a: 'b' });
assign({}, { a: 'b' }).a; // $ExpectType string
assign({ a: 6 }, { a: 'b' }).a; // $ExpectType string
assign({ a: 6 }, {}).a; // $ExpectType number
assign({ b: 6 }, {}).a; // $ExpectError
assign({}, { b: 6 }, {}).b; // $ExpectType number
@ -12,12 +13,17 @@ import { assign, merge } from '@ember/polyfills';
assign({ a: 'hello' }, '', { a: true }).a; // $ExpectError
assign({ d: ['gobias industries'] }, { a: 'hello' }, { b: 6 }, { a: true }).d; // $ExpectType string[]
assign({}, { a: 0 }, { b: 1 }, { c: 2 }, { d: 3 }).a; // $ExpectType any
// matches Object.assign
assign({}, null); // $ExpectType never
assign({}, undefined); // $ExpectType never
})();
(() => { /* merge */
merge({}, { a: 'b'});
merge({}, { a: 'b'}).a; // $ExpectType string
merge({ a: 6 }, { a: 'b'}).a; // $ExpectType string
(() => {
/* merge */
merge({}, { a: 'b' });
merge({}, { a: 'b' }).a; // $ExpectType string
merge({ a: 6 }, { a: 'b' }).a; // $ExpectType string
merge({ a: 6 }, {}).a; // $ExpectType number
merge({ b: 6 }, {}).a; // $ExpectError
})();

View File

@ -13,6 +13,7 @@ export function assign<T extends object, U extends object>(target: T, source: U)
export function assign<T extends object, U extends object, V extends object>(target: T, source1: U, source2: V): Mix3<T, U, V>;
export function assign<T extends object, U extends object, V extends object, W extends object>(target: T, source1: U, source2: V, source3: W): Mix4<T, U, V, W>;
export function assign(target: object, ...sources: object[]): any;
export function assign(target: object, final: undefined | null): never;
/**
* Merge the contents of two objects together into the first object.

View File

@ -2,6 +2,13 @@ export type KeysOfType<Base, Condition> = keyof Pick<Base, {
[Key in keyof Base]: Base[Key] extends Condition ? Key : never
}[keyof Base]>;
// Since `TypeLookup` resolves all *other* types, including `null` and
// `undefined`, we can assume that if the type does *not* resolve from
// `KeysOfType`, it is safe to treat it as 'object'.
export type TypeOf<Base, Condition> = KeysOfType<Base, Condition> extends never
? 'object'
: KeysOfType<Base, Condition>;
export interface TypeLookup {
string: string;
number: number;

View File

@ -1,13 +1,4 @@
import {
compare,
isBlank,
isEmpty,
isEqual,
isNone,
isPresent,
tryInvoke,
typeOf
} from '@ember/utils';
import { compare, isBlank, isEmpty, isEqual, isNone, isPresent, tryInvoke, typeOf } from '@ember/utils';
(function() {
/** isNone */
@ -16,11 +7,11 @@ import {
return;
}
const anotherString = maybeUndefined + 'another string';
isNone(); // $ExpectType boolean
isNone(null); // $ExpectType boolean
isNone(undefined); // $ExpectType boolean
isNone(''); // $ExpectType boolean
isNone([]); // $ExpectType boolean
isNone(); // $ExpectType boolean
isNone(null); // $ExpectType boolean
isNone(undefined); // $ExpectType boolean
isNone(''); // $ExpectType boolean
isNone([]); // $ExpectType boolean
isNone(function() {}); // $ExpectType boolean
})();
@ -28,8 +19,8 @@ import {
/** tryInvoke */
let d = new Date('03/15/2013');
tryInvoke(d, 'getTime'); // $ExpectType number
tryInvoke(d, 'setFullYear', [2014]); // $ExpectType number
tryInvoke(d, 'getTime'); // $ExpectType number
tryInvoke(d, 'setFullYear', [2014]); // $ExpectType number
tryInvoke(d, 'noSuchMethod', [2014]); // $ExpectType undefined
tryInvoke(d, 'getTime');
tryInvoke(d, 'setFullYear', [2014]);
@ -38,46 +29,47 @@ import {
(function() {
/** isPresent */
isPresent(); // $ExpectType boolean
isPresent(null); // $ExpectType boolean
isPresent(undefined); // $ExpectType boolean
isPresent(''); // $ExpectType boolean
isPresent(' '); // $ExpectType boolean
isPresent('\n\t'); // $ExpectType boolean
isPresent([]); // $ExpectType boolean
isPresent({ length: 0 }); // $ExpectType boolean
isPresent(false); // $ExpectType boolean
isPresent(true); // $ExpectType boolean
isPresent('string'); // $ExpectType boolean
isPresent(0); // $ExpectType boolean
isPresent(function() {}); // $ExpectType boolean
isPresent({}); // $ExpectType boolean
isPresent(false); // $ExpectType boolean
isPresent('\n\t Hello'); // $ExpectType boolean
isPresent([1, 2, 3]); // $ExpectType boolean
isPresent(); // $ExpectType boolean
isPresent(null); // $ExpectType boolean
isPresent(undefined); // $ExpectType boolean
isPresent(''); // $ExpectType boolean
isPresent(' '); // $ExpectType boolean
isPresent('\n\t'); // $ExpectType boolean
isPresent([]); // $ExpectType boolean
isPresent({ length: 0 }); // $ExpectType boolean
isPresent(false); // $ExpectType boolean
isPresent(true); // $ExpectType boolean
isPresent('string'); // $ExpectType boolean
isPresent(0); // $ExpectType boolean
isPresent(function() {}); // $ExpectType boolean
isPresent({}); // $ExpectType boolean
isPresent(false); // $ExpectType boolean
isPresent('\n\t Hello'); // $ExpectType boolean
isPresent([1, 2, 3]); // $ExpectType boolean
})();
(function() {
/** typeOf */
typeOf(null); // $ExpectType "null"
typeOf(undefined); // $ExpectType "undefined"
typeOf('michael'); // $ExpectType "string"
typeOf(null); // $ExpectType "null"
typeOf(undefined); // $ExpectType "undefined"
typeOf('michael'); // $ExpectType "string"
// tslint:disable-next-line:no-construct
typeOf(new String('michael')); // $ExpectType "string"
typeOf(101); // $ExpectType "number"
typeOf(new String('michael')); // $ExpectType "string"
typeOf(101); // $ExpectType "number"
// tslint:disable-next-line:no-construct
typeOf(new Number(101)); // $ExpectType "number"
typeOf(true); // $ExpectType "boolean"
typeOf(new Number(101)); // $ExpectType "number"
typeOf(true); // $ExpectType "boolean"
// tslint:disable-next-line:no-construct
typeOf(new Boolean(true)); // $ExpectType "boolean"
typeOf(() => 4); // $ExpectType "function"
typeOf([1, 2, 90]); // $ExpectType "array"
typeOf(/abc/); // $ExpectType "regexp"
typeOf(new Date()); // $ExpectType "date"
typeOf(new FileList()); // $ExpectType "filelist"
typeOf(new Boolean(true)); // $ExpectType "boolean"
typeOf(() => 4); // $ExpectType "function"
typeOf([1, 2, 90]); // $ExpectType "array"
typeOf(/abc/); // $ExpectType "regexp"
typeOf(new Date()); // $ExpectType "date"
typeOf(new FileList()); // $ExpectType "filelist"
// typeOf(EmberObject.extend()); // $ExpectType "class"
// typeOf(EmberObject.create()); // $ExpectType "instance"
typeOf(new Error('teamocil')); // $ExpectType "error"
typeOf(new Error('teamocil')); // $ExpectType "error"
typeOf({ justAPojo: true }); // $ExpectType "object"
typeOf();
typeOf(null);

View File

@ -4,7 +4,7 @@
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.8
import { TypeLookup, KeysOfType, FunctionArgs } from "./-private/types";
import { TypeLookup, TypeOf, FunctionArgs } from './-private/types';
/**
* Compares two javascript values and returns:
@ -55,6 +55,6 @@ export function tryInvoke(obj: object, methodName: string, args?: any[]): undefi
/**
* Returns a consistent type for the passed object.
*/
export function typeOf<T>(value: T): KeysOfType<TypeLookup, T>;
export function typeOf<T>(value: T): TypeOf<TypeLookup, T>;
export function typeOf(): 'undefined';
export function typeOf(item: any): string;