Hone logo
Hone
Problems

Robust Type Checking in Angular Components

Angular's type system is a powerful tool for catching errors early. This challenge focuses on enhancing type safety within Angular components by implementing a custom type checker for component inputs. You'll build a decorator that validates the type of input properties against a specified type, providing informative error messages if a mismatch is detected.

Problem Description

You are tasked with creating an @TypeCheck decorator for Angular components. This decorator should be applied to component input properties and enforce type checking at runtime. When a component is initialized, the decorator should verify that the value passed to the decorated input property matches the declared type. If a type mismatch occurs, a clear error message should be logged to the console, preventing unexpected behavior and improving code maintainability.

Key Requirements:

  • Decorator Implementation: Create a decorator @TypeCheck that accepts a type as an argument (e.g., @TypeCheck(String)).
  • Runtime Type Checking: The decorator should perform type checking during the component's ngOnInit lifecycle hook.
  • Informative Error Messages: When a type mismatch is detected, the error message should clearly indicate the property name, expected type, and actual type.
  • Component Compatibility: The decorator should be compatible with standard Angular component input properties.
  • No External Libraries: Solve this problem using only built-in TypeScript and Angular features.

Expected Behavior:

  1. When a component is initialized, the @TypeCheck decorator should inspect its decorated input properties.
  2. For each decorated property, the decorator should compare the provided value with the declared type.
  3. If the types match, no action is required.
  4. If the types mismatch, an error message should be logged to the console.
  5. The component should still render and function, even if type errors are detected (the decorator should not prevent the component from initializing).

Edge Cases to Consider:

  • Null and Undefined: How should the decorator handle null and undefined values? Should they be considered valid for nullable types?
  • Complex Types: How should the decorator handle complex types like arrays, objects, and custom types? (For simplicity, focus on primitive types like string, number, boolean, and potentially arrays of these types).
  • Optional Properties: How should the decorator handle optional properties (those with a ? in their type definition)?
  • Typeof vs. Instanceof: Consider the appropriate method for type checking (e.g., typeof for primitives, instanceof for objects).

Examples

Example 1:

// Component code
import { Component, Input, OnInit, AfterViewInit } from '@TypeCheck';

@TypeCheck(String)
@Component({
  selector: 'app-my-component',
  template: '<div>Hello, {{ name }}</div>'
})
export class MyComponent implements OnInit {
  @TypeCheck(Number)
  @Input() name: string = 'World';
  @Input() age: number = 30;

  ngOnInit() {
    console.log('Component initialized');
  }
}
Input:  The component above is initialized with name = "test" and age = "abc"
Output: Console logs:
"TypeError: 'abc' is not of type 'number'. Property: age"
"TypeError: 'test' is not of type 'string'. Property: name"
Explanation: The decorator detects type mismatches for both 'name' and 'age' and logs appropriate error messages.

Example 2:

// Component code
import { Component, Input, OnInit } from '@TypeCheck';

@Component({
  selector: 'app-my-component',
  template: '<div>Value: {{ value }}</div>'
})
export class MyComponent implements OnInit {
  @TypeCheck(Number)
  @Input() value: number = 10;

  ngOnInit() {
    console.log('Component initialized');
  }
}
Input: The component above is initialized with value = 20
Output: No console logs.
Explanation: The type of 'value' matches the declared type (Number), so no error message is logged.

Example 3:

// Component code
import { Component, Input, OnInit } from '@TypeCheck';

@Component({
  selector: 'app-my-component',
  template: '<div>Array: {{ myArray }}</div>'
})
export class MyComponent implements OnInit {
  @TypeCheck(Array<String>)
  @Input() myArray: string[] = ["a", "b"];

  ngOnInit() {
    console.log('Component initialized');
  }
}
Input: The component above is initialized with myArray = [1, 2];
Output: Console logs:
"TypeError: 1 is not of type 'string'. Property: myArray[0]"
"TypeError: 2 is not of type 'string'. Property: myArray[1]"
Explanation: The decorator detects that the array elements are not strings and logs appropriate error messages.

Constraints

  • Performance: The type checking should be reasonably efficient. Avoid complex or computationally expensive operations.
  • Primitive Types: Focus on type checking for string, number, boolean, and arrays of these types. Object type checking is not required for this challenge.
  • No External Dependencies: The solution must not rely on any external libraries beyond Angular and TypeScript's built-in features.
  • Error Message Clarity: Error messages must be clear and informative, indicating the property name and the type mismatch.

Notes

  • Consider using TypeScript's typeof operator for primitive type checking.
  • The decorator should be applied to input properties before the component is initialized.
  • Think about how to handle optional properties gracefully.
  • The goal is to provide a basic level of type checking at runtime. This is not intended to replace comprehensive type checking during development.
  • You'll need to leverage Angular's metadata to access the input property's type. Look into @Input() decorator metadata.
Loading editor...
typescript