From f5c7e883a9372aa243e21759ad33752f2530629b Mon Sep 17 00:00:00 2001 From: Misko Hevery Date: Mon, 9 Mar 2020 09:54:51 -0700 Subject: [PATCH] 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 --- packages/core/src/render3/assert.ts | 17 ++++++++++++----- packages/core/src/render3/node_assert.ts | 18 +++++++++++++++--- packages/core/src/util/assert.ts | 22 ++++++++++++---------- 3 files changed, 39 insertions(+), 18 deletions(-) diff --git a/packages/core/src/render3/assert.ts b/packages/core/src/render3/assert.ts index d639f88f2f..186b0fde77 100644 --- a/packages/core/src/render3/assert.ts +++ b/packages/core/src/render3/assert.ts @@ -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(obj: any): asserts obj is DirectiveDef { 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.`); diff --git a/packages/core/src/render3/node_assert.ts b/packages/core/src/render3/node_assert.ts index ce8ef229f4..722c8f4fdc 100644 --- a/packages/core/src/render3/node_assert.ts +++ b/packages/core/src/render3/node_assert.ts @@ -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( diff --git a/packages/core/src/util/assert.ts b/packages/core/src/util/assert.ts index 80d28f0ef8..c713d19910 100644 --- a/packages/core/src/util/assert.ts +++ b/packages/core/src/util/assert.ts @@ -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(actual: T, expected: T, msg: string) { } } -export function assertNotEqual(actual: T, expected: T, msg: string) { +export function assertNotEqual(actual: T, expected: T, msg: string): asserts actual is T { if (!(actual != expected)) { throwError(msg, actual, expected, '!='); } } -export function assertSame(actual: T, expected: T, msg: string) { +export function assertSame(actual: T, expected: T, msg: string): asserts actual is T { if (!(actual === expected)) { throwError(msg, actual, expected, '==='); } @@ -54,25 +55,26 @@ export function assertNotSame(actual: T, expected: T, msg: string) { } } -export function assertLessThan(actual: T, expected: T, msg: string) { +export function assertLessThan(actual: T, expected: T, msg: string): asserts actual is T { if (!(actual < expected)) { throwError(msg, actual, expected, '<'); } } -export function assertLessThanOrEqual(actual: T, expected: T, msg: string) { +export function assertLessThanOrEqual(actual: T, expected: T, msg: string): asserts actual is T { if (!(actual <= expected)) { throwError(msg, actual, expected, '<='); } } -export function assertGreaterThan(actual: T, expected: T, msg: string) { +export function assertGreaterThan(actual: T, expected: T, msg: string): asserts actual is T { if (!(actual > expected)) { throwError(msg, actual, expected, '>'); } } -export function assertGreaterThanOrEqual(actual: T, expected: T, msg: string) { +export function assertGreaterThanOrEqual( + 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) ||