Skip to content

Commit 60ed00a

Browse files
devversiondylhunn
authored andcommitted
refactor(core): improve API documentation for input after angular.dev support (#55053)
This commit improves the API documentation for `input` after we added support for initializer APIs in angular.dev docs generation. Changes: - Rename `ReadT` to `T`. This conceptually makes it easy to talk about inputs of type `T` if there is no transform involved. The common case. - Rename `WriteT` to `TransformT`. This makes it clear that this is the type that the "transform" needs to handle. - Improves the "overall" description of the input function so that it can be shown as a general overview for the API site. - Improves usage notes to be a little more helpful, yielding more useful content in the API docs usage notes section. - Add short JSDoc description for each individual overload. PR Close #55053
1 parent c7ff3d1 commit 60ed00a

File tree

6 files changed

+102
-84
lines changed

6 files changed

+102
-84
lines changed

goldens/public-api/core/index.md

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -937,43 +937,41 @@ export interface InputDecorator {
937937

938938
// @public
939939
export interface InputFunction {
940-
<ReadT>(): InputSignal<ReadT | undefined>;
941-
// (undocumented)
942-
<ReadT>(initialValue: ReadT, opts?: InputOptionsWithoutTransform<ReadT>): InputSignal<ReadT>;
943-
// (undocumented)
944-
<ReadT, WriteT>(initialValue: ReadT, opts: InputOptionsWithTransform<ReadT, WriteT>): InputSignalWithTransform<ReadT, WriteT>;
940+
<T>(): InputSignal<T | undefined>;
941+
<T>(initialValue: T, opts?: InputOptionsWithoutTransform<T>): InputSignal<T>;
942+
<T, TransformT>(initialValue: T, opts: InputOptionsWithTransform<T, TransformT>): InputSignalWithTransform<T, TransformT>;
945943
required: {
946-
<ReadT>(opts?: InputOptionsWithoutTransform<ReadT>): InputSignal<ReadT>;
947-
<ReadT, WriteT>(opts: InputOptionsWithTransform<ReadT, WriteT>): InputSignalWithTransform<ReadT, WriteT>;
944+
<T>(opts?: InputOptionsWithoutTransform<T>): InputSignal<T>;
945+
<T, TransformT>(opts: InputOptionsWithTransform<T, TransformT>): InputSignalWithTransform<T, TransformT>;
948946
};
949947
}
950948

951949
// @public
952-
export interface InputOptions<ReadT, WriteT> {
950+
export interface InputOptions<T, TransformT> {
953951
alias?: string;
954-
transform?: (v: WriteT) => ReadT;
952+
transform?: (v: TransformT) => T;
955953
}
956954

957955
// @public
958-
export type InputOptionsWithoutTransform<ReadT> = Omit<InputOptions<ReadT, ReadT>, 'transform'> & {
956+
export type InputOptionsWithoutTransform<T> = Omit<InputOptions<T, T>, 'transform'> & {
959957
transform?: undefined;
960958
};
961959

962960
// @public
963-
export type InputOptionsWithTransform<ReadT, WriteT> = Required<Pick<InputOptions<ReadT, WriteT>, 'transform'>> & InputOptions<ReadT, WriteT>;
961+
export type InputOptionsWithTransform<T, TransformT> = Required<Pick<InputOptions<T, TransformT>, 'transform'>> & InputOptions<T, TransformT>;
964962

965963
// @public
966-
export interface InputSignal<ReadT> extends InputSignalWithTransform<ReadT, ReadT> {
964+
export interface InputSignal<T> extends InputSignalWithTransform<T, T> {
967965
}
968966

969967
// @public
970-
export interface InputSignalWithTransform<ReadT, WriteT> extends Signal<ReadT> {
968+
export interface InputSignalWithTransform<T, TransformT> extends Signal<T> {
971969
// (undocumented)
972-
INPUT_SIGNAL_BRAND_READ_TYPE]: ReadT;
970+
INPUT_SIGNAL_BRAND_READ_TYPE]: T;
973971
// (undocumented)
974-
INPUT_SIGNAL_BRAND_WRITE_TYPE]: WriteT;
972+
INPUT_SIGNAL_BRAND_WRITE_TYPE]: TransformT;
975973
// (undocumented)
976-
[SIGNAL]: InputSignalNode<ReadT, WriteT>;
974+
[SIGNAL]: InputSignalNode<T, TransformT>;
977975
}
978976

979977
// @public
@@ -1101,9 +1099,11 @@ export const model: ModelFunction;
11011099
// @public
11021100
export interface ModelFunction {
11031101
<T>(): ModelSignal<T | undefined>;
1104-
// (undocumented)
11051102
<T>(initialValue: T, opts?: ModelOptions): ModelSignal<T>;
1106-
required<T>(opts?: ModelOptions): ModelSignal<T>;
1103+
// (undocumented)
1104+
required: {
1105+
<T>(opts?: ModelOptions): ModelSignal<T>;
1106+
};
11071107
}
11081108

11091109
// @public

packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -722,7 +722,7 @@ class TcbDirectiveInputsOp extends TcbOp {
722722
// For signal inputs, a `transformType` will never be set as we do not capture
723723
// the transform in the compiler metadata. Signal inputs incorporate their
724724
// transform write type into their member type, and we extract it below when
725-
// unwrapping the `InputSignal<ReadT, WriteT>`.
725+
// setting the `WriteT` of such `InputSignalWithTransform<_, WriteT>`.
726726

727727
if (this.dir.coercedInputFields.has(fieldName)) {
728728
let type: ts.TypeNode;

packages/core/src/authoring/input/input.ts

Lines changed: 61 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export function inputFunction<ReadT, WriteT>(
1818
return createInputSignal(initialValue, opts);
1919
}
2020

21-
export function inputRequiredFunction<ReadT, WriteT>(opts?: InputOptions<ReadT, WriteT>):
21+
export function inputRequiredFunction<ReadT, WriteT = ReadT>(opts?: InputOptions<ReadT, WriteT>):
2222
InputSignalWithTransform<ReadT, WriteT> {
2323
ngDevMode && assertInInjectionContext(input);
2424
return createInputSignal(REQUIRED_UNSET_VALUE as never, opts);
@@ -31,77 +31,95 @@ export function inputRequiredFunction<ReadT, WriteT>(opts?: InputOptions<ReadT,
3131
* The function exposes an API for also declaring required inputs via the
3232
* `input.required` function.
3333
*
34-
* @usageNotes
35-
* Initialize an input in your directive or component by declaring a
36-
* class field and initializing it with the `input()` or `input.required()`
37-
* function.
38-
*
39-
* ```ts
40-
* @Directive({..})
41-
* export class MyDir {
42-
* firstName = input<string>(); // string|undefined
43-
* lastName = input.required<string>(); // string
44-
* age = input(0); // number
45-
* }
46-
* ```
47-
*
4834
* @developerPreview
4935
*/
5036
export interface InputFunction {
5137
/**
52-
* Initializes an input with an initial value. If no explicit value
53-
* is specified, Angular will use `undefined`.
54-
*
55-
* Consider using `input.required` for inputs that don't need an
56-
* initial value.
38+
* Initializes an input of type `T` with an initial value of `undefined`.
39+
* Angular will implicitly use `undefined` as initial value.
40+
*/
41+
<T>(): InputSignal<T|undefined>;
42+
/** Declares an input of type `T` with an explicit initial value. */
43+
<T>(initialValue: T, opts?: InputOptionsWithoutTransform<T>): InputSignal<T>;
44+
/**
45+
* Declares an input of type `T` with an initial value and a transform
46+
* function.
5747
*
58-
* @developerPreview
48+
* The input accepts values of type `TransformT` and the given
49+
* transform function will transform the value to type `T`.
5950
*/
60-
<ReadT>(): InputSignal<ReadT|undefined>;
61-
<ReadT>(initialValue: ReadT, opts?: InputOptionsWithoutTransform<ReadT>): InputSignal<ReadT>;
62-
<ReadT, WriteT>(initialValue: ReadT, opts: InputOptionsWithTransform<ReadT, WriteT>):
63-
InputSignalWithTransform<ReadT, WriteT>;
51+
<T, TransformT>(initialValue: T, opts: InputOptionsWithTransform<T, TransformT>):
52+
InputSignalWithTransform<T, TransformT>;
6453

6554
/**
6655
* Initializes a required input.
6756
*
68-
* Users of your directive/component need to bind to this
57+
* Consumers of your directive/component need to bind to this
6958
* input. If unset, a compile time error will be reported.
7059
*
7160
* @developerPreview
7261
*/
7362
required: {
74-
<ReadT>(opts?: InputOptionsWithoutTransform<ReadT>): InputSignal<ReadT>;
75-
76-
<ReadT, WriteT>(opts: InputOptionsWithTransform<ReadT, WriteT>):
77-
InputSignalWithTransform<ReadT, WriteT>;
63+
/** Declares a required input of type `T`. */
64+
<T>(opts?: InputOptionsWithoutTransform<T>): InputSignal<T>;
65+
/**
66+
* Declares a required input of type `T` with a transform function.
67+
*
68+
* The input accepts values of type `TransformT` and the given
69+
* transform function will transform the value to type `T`.
70+
*/
71+
<T, TransformT>(opts: InputOptionsWithTransform<T, TransformT>):
72+
InputSignalWithTransform<T, TransformT>;
7873
};
7974
}
8075

8176
/**
82-
* The `input` function allows declaration of inputs in directives and
83-
* components.
77+
* The `input` function allows declaration of Angular inputs in directives
78+
* and components.
79+
*
80+
* There are two variants of inputs that can be declared:
8481
*
85-
* Initializes an input with an initial value. If no explicit value
86-
* is specified, Angular will use `undefined`.
82+
* 1. **Optional inputs** with an initial value.
83+
* 2. **Required inputs** that consumers need to set.
8784
*
88-
* Consider using `input.required` for inputs that don't need an
89-
* initial value.
85+
* By default, the `input` function will declare optional inputs that
86+
* always have an initial value. Required inputs can be declared
87+
* using the `input.required()` function.
88+
*
89+
* Inputs are signals. The values of an input are exposed as a `Signal`.
90+
* The signal always holds the latest value of the input that is bound
91+
* from the parent.
9092
*
9193
* @usageNotes
92-
* Initialize an input in your directive or component by declaring a
93-
* class field and initializing it with the `input()` function.
94+
* To use signal-based inputs, import `input` from `@angular/core`.
95+
*
96+
* ```
97+
* import {input} from '@angular/core`;
98+
* ```
99+
*
100+
* Inside your component, introduce a new class member and initialize
101+
* it with a call to `input` or `input.required`.
94102
*
95103
* ```ts
96-
* @Directive({..})
97-
* export class MyDir {
98-
* firstName = input<string>(); // string|undefined
99-
* lastName = input.required<string>(); // string
100-
* age = input(0); // number
104+
* @Component({
105+
* ...
106+
* })
107+
* export class UserProfileComponent {
108+
* firstName = input<string>(); // Signal<string|undefined>
109+
* lastName = input.required<string>(); // Signal<string>
110+
* age = input(0) // Signal<number>
101111
* }
102112
* ```
103113
*
114+
* Inside your component template, you can display values of the inputs
115+
* by calling the signal.
116+
*
117+
* ```html
118+
* <span>{{firstName()}}</span>
119+
* ```
120+
*
104121
* @developerPreview
122+
* @initializerApiFunction
105123
*/
106124
export const input: InputFunction = (() => {
107125
// Note: This may be considered a side-effect, but nothing will depend on

packages/core/src/authoring/input/input_signal.ts

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {INPUT_SIGNAL_NODE, InputSignalNode, REQUIRED_UNSET_VALUE} from './input_
1818
*
1919
* Options for signal inputs.
2020
*/
21-
export interface InputOptions<ReadT, WriteT> {
21+
export interface InputOptions<T, TransformT> {
2222
/** Optional public name for the input. By default, the class field name is used. */
2323
alias?: string;
2424
/**
@@ -31,24 +31,24 @@ export interface InputOptions<ReadT, WriteT> {
3131
* attribute form to bind to the input via `<my-dir input>`. A transform can then
3232
* handle such string values and convert them to `boolean`. See: {@link booleanAttribute}.
3333
*/
34-
transform?: (v: WriteT) => ReadT;
34+
transform?: (v: TransformT) => T;
3535
}
3636

3737
/**
3838
* Signal input options without the transform option.
3939
*
4040
* @developerPreview
4141
*/
42-
export type InputOptionsWithoutTransform<ReadT> =
42+
export type InputOptionsWithoutTransform<T> =
4343
// Note: We still keep a notion of `transform` for auto-completion.
44-
Omit<InputOptions<ReadT, ReadT>, 'transform'>&{transform?: undefined};
44+
Omit<InputOptions<T, T>, 'transform'>&{transform?: undefined};
4545
/**
4646
* Signal input options with the transform option required.
4747
*
4848
* @developerPreview
4949
*/
50-
export type InputOptionsWithTransform<ReadT, WriteT> =
51-
Required<Pick<InputOptions<ReadT, WriteT>, 'transform'>>&InputOptions<ReadT, WriteT>;
50+
export type InputOptionsWithTransform<T, TransformT> =
51+
Required<Pick<InputOptions<T, TransformT>, 'transform'>>&InputOptions<T, TransformT>;
5252

5353
export const ɵINPUT_SIGNAL_BRAND_READ_TYPE = /* @__PURE__ */ Symbol();
5454
export const ɵINPUT_SIGNAL_BRAND_WRITE_TYPE = /* @__PURE__ */ Symbol();
@@ -77,10 +77,10 @@ export const ɵINPUT_SIGNAL_BRAND_WRITE_TYPE = /* @__PURE__ */ Symbol();
7777
*
7878
* @developerPreview
7979
*/
80-
export interface InputSignalWithTransform<ReadT, WriteT> extends Signal<ReadT> {
81-
[SIGNAL]: InputSignalNode<ReadT, WriteT>;
82-
[ɵINPUT_SIGNAL_BRAND_READ_TYPE]: ReadT;
83-
[ɵINPUT_SIGNAL_BRAND_WRITE_TYPE]: WriteT;
80+
export interface InputSignalWithTransform<T, TransformT> extends Signal<T> {
81+
[SIGNAL]: InputSignalNode<T, TransformT>;
82+
[ɵINPUT_SIGNAL_BRAND_READ_TYPE]: T;
83+
[ɵINPUT_SIGNAL_BRAND_WRITE_TYPE]: TransformT;
8484
}
8585

8686
/**
@@ -94,7 +94,7 @@ export interface InputSignalWithTransform<ReadT, WriteT> extends Signal<ReadT> {
9494
*
9595
* @developerPreview
9696
*/
97-
export interface InputSignal<ReadT> extends InputSignalWithTransform<ReadT, ReadT> {}
97+
export interface InputSignal<T> extends InputSignalWithTransform<T, T> {}
9898

9999
/**
100100
* Creates an input signal.
@@ -103,10 +103,10 @@ export interface InputSignal<ReadT> extends InputSignalWithTransform<ReadT, Read
103103
* Can be set to {@link REQUIRED_UNSET_VALUE} for required inputs.
104104
* @param options Additional options for the input. e.g. a transform, or an alias.
105105
*/
106-
export function createInputSignal<ReadT, WriteT>(
107-
initialValue: ReadT,
108-
options?: InputOptions<ReadT, WriteT>): InputSignalWithTransform<ReadT, WriteT> {
109-
const node: InputSignalNode<ReadT, WriteT> = Object.create(INPUT_SIGNAL_NODE);
106+
export function createInputSignal<T, TransformT>(
107+
initialValue: T,
108+
options?: InputOptions<T, TransformT>): InputSignalWithTransform<T, TransformT> {
109+
const node: InputSignalNode<T, TransformT> = Object.create(INPUT_SIGNAL_NODE);
110110

111111
node.value = initialValue;
112112

@@ -133,5 +133,5 @@ export function createInputSignal<ReadT, WriteT>(
133133
inputValueFn.toString = () => `[Input Signal: ${inputValueFn()}]`;
134134
}
135135

136-
return inputValueFn as InputSignalWithTransform<ReadT, WriteT>;
136+
return inputValueFn as InputSignalWithTransform<T, TransformT>;
137137
}

packages/core/src/authoring/input/input_signal_node.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,22 @@ export const REQUIRED_UNSET_VALUE = /* @__PURE__ */ Symbol('InputSignalNode#UNSE
1414
* Reactive node type for an input signal. An input signal extends a signal.
1515
* There are special properties to enable transforms and required inputs.
1616
*/
17-
export interface InputSignalNode<ReadT, WriteT> extends SignalNode<ReadT> {
17+
export interface InputSignalNode<T, TransformT> extends SignalNode<T> {
1818
/**
1919
* User-configured transform that will run whenever a new value is applied
2020
* to the input signal node.
2121
*/
22-
transformFn: ((value: WriteT) => ReadT)|undefined;
22+
transformFn: ((value: TransformT) => T)|undefined;
2323

2424
/**
2525
* Applies a new value to the input signal. Expects transforms to be run
2626
* manually before.
2727
*
2828
* This function is called by the framework runtime code whenever a binding
2929
* changes. The value can in practice be anything at runtime, but for typing
30-
* purposes we assume it's a valid `ReadT` value. Type-checking will enforce that.
30+
* purposes we assume it's a valid `T` value. Type-checking will enforce that.
3131
*/
32-
applyValueToInputSignal<ReadT, WriteT>(node: InputSignalNode<ReadT, WriteT>, value: ReadT): void;
32+
applyValueToInputSignal<T, TransformT>(node: InputSignalNode<T, TransformT>, value: T): void;
3333
}
3434

3535
// Note: Using an IIFE here to ensure that the spread assignment is not considered
@@ -40,7 +40,7 @@ export const INPUT_SIGNAL_NODE: InputSignalNode<unknown, unknown> = /* @__PURE__
4040
...SIGNAL_NODE,
4141
transformFn: undefined,
4242

43-
applyValueToInputSignal<ReadT, WriteT>(node: InputSignalNode<ReadT, WriteT>, value: ReadT) {
43+
applyValueToInputSignal<T, TransformT>(node: InputSignalNode<T, TransformT>, value: T) {
4444
signalSetFn(node, value);
4545
}
4646
};

packages/core/src/authoring/input/input_type_checking.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import {InputSignalWithTransform} from './input_signal';
1010

11-
/** Retrieves the `WriteT` of an `InputSignal` and `InputSignalWithTransform`. */
11+
/** Retrieves the write type of an `InputSignal` and `InputSignalWithTransform`. */
1212
export type ɵUnwrapInputSignalWriteType<Field> =
1313
Field extends InputSignalWithTransform<any, infer WriteT>? WriteT : never;
1414

0 commit comments

Comments
 (0)