2

I created this simple component that has two inputs that both accept a string and in theory should accept undefined because they have a default value.

@Component({
  selector: 'app-child',
  imports: [],
  template: `
            <p>testString: {{ testString() }}</p>
            <p>testStringTransform: {{ testStringTransform() }}</p>
            `
})
export class Child {
  testString = input<string>("defaultString")
  testStringTransform = input<string, string | undefined>("defaultStringTransform", { transform: (v) => v ?? "defaultStringTransform" })
}

In the parent component I use it like shown in the following example:

<app-child [testString]="'inputString'" [testStringTransform]="undefined" />

This gives me the following output.

<app-child>
  <p>testString: inputString</p>
  <p>testStringTransform: defaultStringTransform</p>
</app-child>

It uses the default value for testStringTransform and the passed input value for testString. I would expect testString to work exactly like testStringTransform if I pass "undefined" like this:

<app-child [testString]="undefined" [testStringTransform]="undefined" />

Actually I am getting a type error:

TS2322: Type 'undefined' is not assignable to type 'string'.

This exmaple might look silly but it makes more sense if you have optional chaining. For example:

<app-child [testString]="testObject?.stringValue" [testStringTransform]="undefined" />

I'm aware that I could do non-null assertions, add type checks, add a computed for every input that returns a default if the value is undefined or use the transform I used in the example. But this is a lot of boilerplate code. Isn't the initialValue of the input signal exactly for this case?

1 Answer 1

1

The error comes from testString which is set to accept only string as an input. If you are going to directly pass in undefined, you must change the definition to string | undefined.

Because those are the types of the values that will be statically passed.

Transform, checks the string | undefined and ensures that the output type is string for the second input signal.

export class Child {
  testString = input<string | undefined>('defaultString');
  testStringTransform = input<string, string | undefined>(
    'defaultStringTransform',
    { transform: (v) => v ?? 'defaultStringTransform' }
  );
}

Full Code:

import 'zone.js';
import { Component, input } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';

@Component({
  selector: 'app-child',
  imports: [],
  template: `
            <p>testString: {{ testString() }}</p>
            <p>testStringTransform: {{ testStringTransform() }}</p>
            `,
})
export class Child {
  testString = input<string | undefined>('defaultString');
  testStringTransform = input<string, string | undefined>(
    'defaultStringTransform',
    { transform: (v) => v ?? 'defaultStringTransform' }
  );
}

@Component({
  selector: 'app-root',
  imports: [Child],
  template: `
    <app-child [testString]="'inputString'" [testStringTransform]="undefined" />
    <app-child [testString]="undefined" [testStringTransform]="undefined" />
  `,
})
export class App {
  name = 'Angular';
}

bootstrapApplication(App);

Stackblitz Demo

Sign up to request clarification or add additional context in comments.

3 Comments

You missed my point of not wanting to type check. If I do this.testString().includes("test") I get Object is possibly 'undefined'.(2532). This might be easy solveable by adding a ? but in other cases it's not that easy.
Oh I just realized in your second example "testString:" is printed instead of "testString: defaultString". I think I confused "initial value" with "default value". Don't know why I didn't realize this when creating the example. So I suppose transform functions are the intended way to handle default values if the input is undefined?
Yes, it is a good way for setting default values and transforming input values to the intended type.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.