refactor(core): Take advantage of 'assert functions' for `ngDevMode` asserts (#35964)

As of TypeScript 3.7, TypeScript supports [Assert Functions](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#assertion-functions). This change adds assert types to our `assert*` functions.

We can't fully take advantage of this due to [Assert functions do not constraint type when they are guarded by a truthy expression.](https://github.com/microsoft/TypeScript/issues/37295)

PR Close #35964
This commit is contained in:
Misko Hevery 2020-03-09 09:54:51 -07:00 committed by Matias Niemelä
parent 15f8afa4bf
commit f5c7e883a9
3 changed files with 39 additions and 18 deletions

View File

@ -9,10 +9,16 @@
import {assertDefined, assertEqual, throwError} from '../util/assert';
import {getComponentDef, getNgModuleDef} from './definition';
import {LContainer} from './interfaces/container';
import {DirectiveDef} from './interfaces/definition';
import {TNode} from './interfaces/node';
import {isLContainer, isLView} from './interfaces/type_checks';
import {LView, TVIEW, TView} from './interfaces/view';
// [Assert functions do not constraint type when they are guarded by a truthy
// expression.](https://github.com/microsoft/TypeScript/issues/37295)
export function assertTNodeForLView(tNode: TNode, lView: LView) {
tNode.hasOwnProperty('tView_') && assertEqual(
(tNode as any as{tView_: TView}).tView_, lView[TVIEW],
@ -50,20 +56,21 @@ export function assertDataNext(lView: LView, index: number, arr?: any[]) {
arr.length, index, `index ${index} expected to be at the end of arr (length ${arr.length})`);
}
export function assertLContainerOrUndefined(value: any): void {
export function assertLContainerOrUndefined(value: any): asserts value is LContainer|undefined|
null {
value && assertEqual(isLContainer(value), true, 'Expecting LContainer or undefined or null');
}
export function assertLContainer(value: any): void {
export function assertLContainer(value: any): asserts value is LContainer {
assertDefined(value, 'LContainer must be defined');
assertEqual(isLContainer(value), true, 'Expecting LContainer');
}
export function assertLViewOrUndefined(value: any): void {
export function assertLViewOrUndefined(value: any): asserts value is LView|null|undefined {
value && assertEqual(isLView(value), true, 'Expecting LView or undefined or null');
}
export function assertLView(value: any) {
export function assertLView(value: any): asserts value is LView {
assertDefined(value, 'LView must be defined');
assertEqual(isLView(value), true, 'Expecting LView');
}
@ -82,7 +89,7 @@ export function assertFirstUpdatePass(tView: TView, errMessage?: string) {
* This is a basic sanity check that an object is probably a directive def. DirectiveDef is
* an interface, so we can't do a direct instanceof check.
*/
export function assertDirectiveDef(obj: any) {
export function assertDirectiveDef<T>(obj: any): asserts obj is DirectiveDef<T> {
if (obj.type === undefined || obj.selectors == undefined || obj.inputs === undefined) {
throwError(
`Expected a DirectiveDef/ComponentDef and this object does not seem to have the expected shape.`);

View File

@ -7,14 +7,26 @@
*/
import {assertDefined, assertEqual} from '../util/assert';
import {TNode, TNodeType} from './interfaces/node';
export function assertNodeType(tNode: TNode, type: TNodeType) {
import {TContainerNode, TElementContainerNode, TElementNode, TIcuContainerNode, TNode, TNodeType, TProjectionNode} from './interfaces/node';
export function assertNodeType(
tNode: TNode, type: TNodeType.Container): asserts tNode is TContainerNode;
export function assertNodeType(
tNode: TNode, type: TNodeType.Element): asserts tNode is TElementNode;
export function assertNodeType(
tNode: TNode, type: TNodeType.ElementContainer): asserts tNode is TElementContainerNode;
export function assertNodeType(
tNode: TNode, type: TNodeType.IcuContainer): asserts tNode is TIcuContainerNode;
export function assertNodeType(
tNode: TNode, type: TNodeType.Projection): asserts tNode is TProjectionNode;
export function assertNodeType(tNode: TNode, type: TNodeType.View): asserts tNode is TContainerNode;
export function assertNodeType(tNode: TNode, type: TNodeType): asserts tNode is TNode {
assertDefined(tNode, 'should be called with a TNode');
assertEqual(tNode.type, type, `should be a ${typeName(type)}`);
}
export function assertNodeOfPossibleTypes(tNode: TNode, ...types: TNodeType[]) {
export function assertNodeOfPossibleTypes(tNode: TNode, ...types: TNodeType[]): void {
assertDefined(tNode, 'should be called with a TNode');
const found = types.some(type => tNode.type === type);
assertEqual(

View File

@ -12,19 +12,20 @@
import {stringify} from './stringify';
export function assertNumber(actual: any, msg: string) {
export function assertNumber(actual: any, msg: string): asserts actual is number {
if (!(typeof actual === 'number')) {
throwError(msg, typeof actual, 'number', '===');
}
}
export function assertNumberInRange(actual: any, minInclusive: number, maxInclusive: number) {
export function assertNumberInRange(
actual: any, minInclusive: number, maxInclusive: number): asserts actual is number {
assertNumber(actual, 'Expected a number');
assertLessThanOrEqual(actual, maxInclusive, 'Expected number to be less than or equal to');
assertGreaterThanOrEqual(actual, minInclusive, 'Expected number to be greater than or equal to');
}
export function assertString(actual: any, msg: string) {
export function assertString(actual: any, msg: string): asserts actual is string {
if (!(typeof actual === 'string')) {
throwError(msg, actual === null ? 'null' : typeof actual, 'string', '===');
}
@ -36,13 +37,13 @@ export function assertEqual<T>(actual: T, expected: T, msg: string) {
}
}
export function assertNotEqual<T>(actual: T, expected: T, msg: string) {
export function assertNotEqual<T>(actual: T, expected: T, msg: string): asserts actual is T {
if (!(actual != expected)) {
throwError(msg, actual, expected, '!=');
}
}
export function assertSame<T>(actual: T, expected: T, msg: string) {
export function assertSame<T>(actual: T, expected: T, msg: string): asserts actual is T {
if (!(actual === expected)) {
throwError(msg, actual, expected, '===');
}
@ -54,25 +55,26 @@ export function assertNotSame<T>(actual: T, expected: T, msg: string) {
}
}
export function assertLessThan<T>(actual: T, expected: T, msg: string) {
export function assertLessThan<T>(actual: T, expected: T, msg: string): asserts actual is T {
if (!(actual < expected)) {
throwError(msg, actual, expected, '<');
}
}
export function assertLessThanOrEqual<T>(actual: T, expected: T, msg: string) {
export function assertLessThanOrEqual<T>(actual: T, expected: T, msg: string): asserts actual is T {
if (!(actual <= expected)) {
throwError(msg, actual, expected, '<=');
}
}
export function assertGreaterThan<T>(actual: T, expected: T, msg: string) {
export function assertGreaterThan<T>(actual: T, expected: T, msg: string): asserts actual is T {
if (!(actual > expected)) {
throwError(msg, actual, expected, '>');
}
}
export function assertGreaterThanOrEqual<T>(actual: T, expected: T, msg: string) {
export function assertGreaterThanOrEqual<T>(
actual: T, expected: T, msg: string): asserts actual is T {
if (!(actual >= expected)) {
throwError(msg, actual, expected, '>=');
}
@ -98,7 +100,7 @@ export function throwError(msg: string, actual?: any, expected?: any, comparison
(comparison == null ? '' : ` [Expected=> ${expected} ${comparison} ${actual} <=Actual]`));
}
export function assertDomNode(node: any) {
export function assertDomNode(node: any): asserts node is Node {
// If we're in a worker, `Node` will not be defined.
assertEqual(
(typeof Node !== 'undefined' && node instanceof Node) ||