mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
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
This commit is contained in:
parent
c7ff3d158a
commit
60ed00a601
6 changed files with 103 additions and 85 deletions
|
|
@ -937,43 +937,41 @@ export interface InputDecorator {
|
|||
|
||||
// @public
|
||||
export interface InputFunction {
|
||||
<ReadT>(): InputSignal<ReadT | undefined>;
|
||||
// (undocumented)
|
||||
<ReadT>(initialValue: ReadT, opts?: InputOptionsWithoutTransform<ReadT>): InputSignal<ReadT>;
|
||||
// (undocumented)
|
||||
<ReadT, WriteT>(initialValue: ReadT, opts: InputOptionsWithTransform<ReadT, WriteT>): InputSignalWithTransform<ReadT, WriteT>;
|
||||
<T>(): InputSignal<T | undefined>;
|
||||
<T>(initialValue: T, opts?: InputOptionsWithoutTransform<T>): InputSignal<T>;
|
||||
<T, TransformT>(initialValue: T, opts: InputOptionsWithTransform<T, TransformT>): InputSignalWithTransform<T, TransformT>;
|
||||
required: {
|
||||
<ReadT>(opts?: InputOptionsWithoutTransform<ReadT>): InputSignal<ReadT>;
|
||||
<ReadT, WriteT>(opts: InputOptionsWithTransform<ReadT, WriteT>): InputSignalWithTransform<ReadT, WriteT>;
|
||||
<T>(opts?: InputOptionsWithoutTransform<T>): InputSignal<T>;
|
||||
<T, TransformT>(opts: InputOptionsWithTransform<T, TransformT>): InputSignalWithTransform<T, TransformT>;
|
||||
};
|
||||
}
|
||||
|
||||
// @public
|
||||
export interface InputOptions<ReadT, WriteT> {
|
||||
export interface InputOptions<T, TransformT> {
|
||||
alias?: string;
|
||||
transform?: (v: WriteT) => ReadT;
|
||||
transform?: (v: TransformT) => T;
|
||||
}
|
||||
|
||||
// @public
|
||||
export type InputOptionsWithoutTransform<ReadT> = Omit<InputOptions<ReadT, ReadT>, 'transform'> & {
|
||||
export type InputOptionsWithoutTransform<T> = Omit<InputOptions<T, T>, 'transform'> & {
|
||||
transform?: undefined;
|
||||
};
|
||||
|
||||
// @public
|
||||
export type InputOptionsWithTransform<ReadT, WriteT> = Required<Pick<InputOptions<ReadT, WriteT>, 'transform'>> & InputOptions<ReadT, WriteT>;
|
||||
export type InputOptionsWithTransform<T, TransformT> = Required<Pick<InputOptions<T, TransformT>, 'transform'>> & InputOptions<T, TransformT>;
|
||||
|
||||
// @public
|
||||
export interface InputSignal<ReadT> extends InputSignalWithTransform<ReadT, ReadT> {
|
||||
export interface InputSignal<T> extends InputSignalWithTransform<T, T> {
|
||||
}
|
||||
|
||||
// @public
|
||||
export interface InputSignalWithTransform<ReadT, WriteT> extends Signal<ReadT> {
|
||||
export interface InputSignalWithTransform<T, TransformT> extends Signal<T> {
|
||||
// (undocumented)
|
||||
[ɵINPUT_SIGNAL_BRAND_READ_TYPE]: ReadT;
|
||||
[ɵINPUT_SIGNAL_BRAND_READ_TYPE]: T;
|
||||
// (undocumented)
|
||||
[ɵINPUT_SIGNAL_BRAND_WRITE_TYPE]: WriteT;
|
||||
[ɵINPUT_SIGNAL_BRAND_WRITE_TYPE]: TransformT;
|
||||
// (undocumented)
|
||||
[SIGNAL]: InputSignalNode<ReadT, WriteT>;
|
||||
[SIGNAL]: InputSignalNode<T, TransformT>;
|
||||
}
|
||||
|
||||
// @public
|
||||
|
|
@ -1101,9 +1099,11 @@ export const model: ModelFunction;
|
|||
// @public
|
||||
export interface ModelFunction {
|
||||
<T>(): ModelSignal<T | undefined>;
|
||||
// (undocumented)
|
||||
<T>(initialValue: T, opts?: ModelOptions): ModelSignal<T>;
|
||||
required<T>(opts?: ModelOptions): ModelSignal<T>;
|
||||
// (undocumented)
|
||||
required: {
|
||||
<T>(opts?: ModelOptions): ModelSignal<T>;
|
||||
};
|
||||
}
|
||||
|
||||
// @public
|
||||
|
|
|
|||
|
|
@ -722,7 +722,7 @@ class TcbDirectiveInputsOp extends TcbOp {
|
|||
// For signal inputs, a `transformType` will never be set as we do not capture
|
||||
// the transform in the compiler metadata. Signal inputs incorporate their
|
||||
// transform write type into their member type, and we extract it below when
|
||||
// unwrapping the `InputSignal<ReadT, WriteT>`.
|
||||
// setting the `WriteT` of such `InputSignalWithTransform<_, WriteT>`.
|
||||
|
||||
if (this.dir.coercedInputFields.has(fieldName)) {
|
||||
let type: ts.TypeNode;
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ export function inputFunction<ReadT, WriteT>(
|
|||
return createInputSignal(initialValue, opts);
|
||||
}
|
||||
|
||||
export function inputRequiredFunction<ReadT, WriteT>(opts?: InputOptions<ReadT, WriteT>):
|
||||
export function inputRequiredFunction<ReadT, WriteT = ReadT>(opts?: InputOptions<ReadT, WriteT>):
|
||||
InputSignalWithTransform<ReadT, WriteT> {
|
||||
ngDevMode && assertInInjectionContext(input);
|
||||
return createInputSignal(REQUIRED_UNSET_VALUE as never, opts);
|
||||
|
|
@ -31,77 +31,95 @@ export function inputRequiredFunction<ReadT, WriteT>(opts?: InputOptions<ReadT,
|
|||
* The function exposes an API for also declaring required inputs via the
|
||||
* `input.required` function.
|
||||
*
|
||||
* @usageNotes
|
||||
* Initialize an input in your directive or component by declaring a
|
||||
* class field and initializing it with the `input()` or `input.required()`
|
||||
* function.
|
||||
*
|
||||
* ```ts
|
||||
* @Directive({..})
|
||||
* export class MyDir {
|
||||
* firstName = input<string>(); // string|undefined
|
||||
* lastName = input.required<string>(); // string
|
||||
* age = input(0); // number
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @developerPreview
|
||||
*/
|
||||
export interface InputFunction {
|
||||
/**
|
||||
* Initializes an input with an initial value. If no explicit value
|
||||
* is specified, Angular will use `undefined`.
|
||||
*
|
||||
* Consider using `input.required` for inputs that don't need an
|
||||
* initial value.
|
||||
*
|
||||
* @developerPreview
|
||||
* Initializes an input of type `T` with an initial value of `undefined`.
|
||||
* Angular will implicitly use `undefined` as initial value.
|
||||
*/
|
||||
<ReadT>(): InputSignal<ReadT|undefined>;
|
||||
<ReadT>(initialValue: ReadT, opts?: InputOptionsWithoutTransform<ReadT>): InputSignal<ReadT>;
|
||||
<ReadT, WriteT>(initialValue: ReadT, opts: InputOptionsWithTransform<ReadT, WriteT>):
|
||||
InputSignalWithTransform<ReadT, WriteT>;
|
||||
<T>(): InputSignal<T|undefined>;
|
||||
/** Declares an input of type `T` with an explicit initial value. */
|
||||
<T>(initialValue: T, opts?: InputOptionsWithoutTransform<T>): InputSignal<T>;
|
||||
/**
|
||||
* Declares an input of type `T` with an initial value and a transform
|
||||
* function.
|
||||
*
|
||||
* The input accepts values of type `TransformT` and the given
|
||||
* transform function will transform the value to type `T`.
|
||||
*/
|
||||
<T, TransformT>(initialValue: T, opts: InputOptionsWithTransform<T, TransformT>):
|
||||
InputSignalWithTransform<T, TransformT>;
|
||||
|
||||
/**
|
||||
* Initializes a required input.
|
||||
*
|
||||
* Users of your directive/component need to bind to this
|
||||
* Consumers of your directive/component need to bind to this
|
||||
* input. If unset, a compile time error will be reported.
|
||||
*
|
||||
* @developerPreview
|
||||
*/
|
||||
required: {
|
||||
<ReadT>(opts?: InputOptionsWithoutTransform<ReadT>): InputSignal<ReadT>;
|
||||
|
||||
<ReadT, WriteT>(opts: InputOptionsWithTransform<ReadT, WriteT>):
|
||||
InputSignalWithTransform<ReadT, WriteT>;
|
||||
/** Declares a required input of type `T`. */
|
||||
<T>(opts?: InputOptionsWithoutTransform<T>): InputSignal<T>;
|
||||
/**
|
||||
* Declares a required input of type `T` with a transform function.
|
||||
*
|
||||
* The input accepts values of type `TransformT` and the given
|
||||
* transform function will transform the value to type `T`.
|
||||
*/
|
||||
<T, TransformT>(opts: InputOptionsWithTransform<T, TransformT>):
|
||||
InputSignalWithTransform<T, TransformT>;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The `input` function allows declaration of inputs in directives and
|
||||
* components.
|
||||
* The `input` function allows declaration of Angular inputs in directives
|
||||
* and components.
|
||||
*
|
||||
* Initializes an input with an initial value. If no explicit value
|
||||
* is specified, Angular will use `undefined`.
|
||||
* There are two variants of inputs that can be declared:
|
||||
*
|
||||
* Consider using `input.required` for inputs that don't need an
|
||||
* initial value.
|
||||
* 1. **Optional inputs** with an initial value.
|
||||
* 2. **Required inputs** that consumers need to set.
|
||||
*
|
||||
* By default, the `input` function will declare optional inputs that
|
||||
* always have an initial value. Required inputs can be declared
|
||||
* using the `input.required()` function.
|
||||
*
|
||||
* Inputs are signals. The values of an input are exposed as a `Signal`.
|
||||
* The signal always holds the latest value of the input that is bound
|
||||
* from the parent.
|
||||
*
|
||||
* @usageNotes
|
||||
* Initialize an input in your directive or component by declaring a
|
||||
* class field and initializing it with the `input()` function.
|
||||
* To use signal-based inputs, import `input` from `@angular/core`.
|
||||
*
|
||||
* ```
|
||||
* import {input} from '@angular/core`;
|
||||
* ```
|
||||
*
|
||||
* Inside your component, introduce a new class member and initialize
|
||||
* it with a call to `input` or `input.required`.
|
||||
*
|
||||
* ```ts
|
||||
* @Directive({..})
|
||||
* export class MyDir {
|
||||
* firstName = input<string>(); // string|undefined
|
||||
* lastName = input.required<string>(); // string
|
||||
* age = input(0); // number
|
||||
* @Component({
|
||||
* ...
|
||||
* })
|
||||
* export class UserProfileComponent {
|
||||
* firstName = input<string>(); // Signal<string|undefined>
|
||||
* lastName = input.required<string>(); // Signal<string>
|
||||
* age = input(0) // Signal<number>
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Inside your component template, you can display values of the inputs
|
||||
* by calling the signal.
|
||||
*
|
||||
* ```html
|
||||
* <span>{{firstName()}}</span>
|
||||
* ```
|
||||
*
|
||||
* @developerPreview
|
||||
* @initializerApiFunction
|
||||
*/
|
||||
export const input: InputFunction = (() => {
|
||||
// Note: This may be considered a side-effect, but nothing will depend on
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import {INPUT_SIGNAL_NODE, InputSignalNode, REQUIRED_UNSET_VALUE} from './input_
|
|||
*
|
||||
* Options for signal inputs.
|
||||
*/
|
||||
export interface InputOptions<ReadT, WriteT> {
|
||||
export interface InputOptions<T, TransformT> {
|
||||
/** Optional public name for the input. By default, the class field name is used. */
|
||||
alias?: string;
|
||||
/**
|
||||
|
|
@ -31,7 +31,7 @@ export interface InputOptions<ReadT, WriteT> {
|
|||
* attribute form to bind to the input via `<my-dir input>`. A transform can then
|
||||
* handle such string values and convert them to `boolean`. See: {@link booleanAttribute}.
|
||||
*/
|
||||
transform?: (v: WriteT) => ReadT;
|
||||
transform?: (v: TransformT) => T;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -39,16 +39,16 @@ export interface InputOptions<ReadT, WriteT> {
|
|||
*
|
||||
* @developerPreview
|
||||
*/
|
||||
export type InputOptionsWithoutTransform<ReadT> =
|
||||
export type InputOptionsWithoutTransform<T> =
|
||||
// Note: We still keep a notion of `transform` for auto-completion.
|
||||
Omit<InputOptions<ReadT, ReadT>, 'transform'>&{transform?: undefined};
|
||||
Omit<InputOptions<T, T>, 'transform'>&{transform?: undefined};
|
||||
/**
|
||||
* Signal input options with the transform option required.
|
||||
*
|
||||
* @developerPreview
|
||||
*/
|
||||
export type InputOptionsWithTransform<ReadT, WriteT> =
|
||||
Required<Pick<InputOptions<ReadT, WriteT>, 'transform'>>&InputOptions<ReadT, WriteT>;
|
||||
export type InputOptionsWithTransform<T, TransformT> =
|
||||
Required<Pick<InputOptions<T, TransformT>, 'transform'>>&InputOptions<T, TransformT>;
|
||||
|
||||
export const ɵINPUT_SIGNAL_BRAND_READ_TYPE = /* @__PURE__ */ Symbol();
|
||||
export const ɵINPUT_SIGNAL_BRAND_WRITE_TYPE = /* @__PURE__ */ Symbol();
|
||||
|
|
@ -77,10 +77,10 @@ export const ɵINPUT_SIGNAL_BRAND_WRITE_TYPE = /* @__PURE__ */ Symbol();
|
|||
*
|
||||
* @developerPreview
|
||||
*/
|
||||
export interface InputSignalWithTransform<ReadT, WriteT> extends Signal<ReadT> {
|
||||
[SIGNAL]: InputSignalNode<ReadT, WriteT>;
|
||||
[ɵINPUT_SIGNAL_BRAND_READ_TYPE]: ReadT;
|
||||
[ɵINPUT_SIGNAL_BRAND_WRITE_TYPE]: WriteT;
|
||||
export interface InputSignalWithTransform<T, TransformT> extends Signal<T> {
|
||||
[SIGNAL]: InputSignalNode<T, TransformT>;
|
||||
[ɵINPUT_SIGNAL_BRAND_READ_TYPE]: T;
|
||||
[ɵINPUT_SIGNAL_BRAND_WRITE_TYPE]: TransformT;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -94,7 +94,7 @@ export interface InputSignalWithTransform<ReadT, WriteT> extends Signal<ReadT> {
|
|||
*
|
||||
* @developerPreview
|
||||
*/
|
||||
export interface InputSignal<ReadT> extends InputSignalWithTransform<ReadT, ReadT> {}
|
||||
export interface InputSignal<T> extends InputSignalWithTransform<T, T> {}
|
||||
|
||||
/**
|
||||
* Creates an input signal.
|
||||
|
|
@ -103,10 +103,10 @@ export interface InputSignal<ReadT> extends InputSignalWithTransform<ReadT, Read
|
|||
* Can be set to {@link REQUIRED_UNSET_VALUE} for required inputs.
|
||||
* @param options Additional options for the input. e.g. a transform, or an alias.
|
||||
*/
|
||||
export function createInputSignal<ReadT, WriteT>(
|
||||
initialValue: ReadT,
|
||||
options?: InputOptions<ReadT, WriteT>): InputSignalWithTransform<ReadT, WriteT> {
|
||||
const node: InputSignalNode<ReadT, WriteT> = Object.create(INPUT_SIGNAL_NODE);
|
||||
export function createInputSignal<T, TransformT>(
|
||||
initialValue: T,
|
||||
options?: InputOptions<T, TransformT>): InputSignalWithTransform<T, TransformT> {
|
||||
const node: InputSignalNode<T, TransformT> = Object.create(INPUT_SIGNAL_NODE);
|
||||
|
||||
node.value = initialValue;
|
||||
|
||||
|
|
@ -133,5 +133,5 @@ export function createInputSignal<ReadT, WriteT>(
|
|||
inputValueFn.toString = () => `[Input Signal: ${inputValueFn()}]`;
|
||||
}
|
||||
|
||||
return inputValueFn as InputSignalWithTransform<ReadT, WriteT>;
|
||||
return inputValueFn as InputSignalWithTransform<T, TransformT>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,12 +14,12 @@ export const REQUIRED_UNSET_VALUE = /* @__PURE__ */ Symbol('InputSignalNode#UNSE
|
|||
* Reactive node type for an input signal. An input signal extends a signal.
|
||||
* There are special properties to enable transforms and required inputs.
|
||||
*/
|
||||
export interface InputSignalNode<ReadT, WriteT> extends SignalNode<ReadT> {
|
||||
export interface InputSignalNode<T, TransformT> extends SignalNode<T> {
|
||||
/**
|
||||
* User-configured transform that will run whenever a new value is applied
|
||||
* to the input signal node.
|
||||
*/
|
||||
transformFn: ((value: WriteT) => ReadT)|undefined;
|
||||
transformFn: ((value: TransformT) => T)|undefined;
|
||||
|
||||
/**
|
||||
* Applies a new value to the input signal. Expects transforms to be run
|
||||
|
|
@ -27,9 +27,9 @@ export interface InputSignalNode<ReadT, WriteT> extends SignalNode<ReadT> {
|
|||
*
|
||||
* This function is called by the framework runtime code whenever a binding
|
||||
* changes. The value can in practice be anything at runtime, but for typing
|
||||
* purposes we assume it's a valid `ReadT` value. Type-checking will enforce that.
|
||||
* purposes we assume it's a valid `T` value. Type-checking will enforce that.
|
||||
*/
|
||||
applyValueToInputSignal<ReadT, WriteT>(node: InputSignalNode<ReadT, WriteT>, value: ReadT): void;
|
||||
applyValueToInputSignal<T, TransformT>(node: InputSignalNode<T, TransformT>, value: T): void;
|
||||
}
|
||||
|
||||
// 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__
|
|||
...SIGNAL_NODE,
|
||||
transformFn: undefined,
|
||||
|
||||
applyValueToInputSignal<ReadT, WriteT>(node: InputSignalNode<ReadT, WriteT>, value: ReadT) {
|
||||
applyValueToInputSignal<T, TransformT>(node: InputSignalNode<T, TransformT>, value: T) {
|
||||
signalSetFn(node, value);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import {InputSignalWithTransform} from './input_signal';
|
||||
|
||||
/** Retrieves the `WriteT` of an `InputSignal` and `InputSignalWithTransform`. */
|
||||
/** Retrieves the write type of an `InputSignal` and `InputSignalWithTransform`. */
|
||||
export type ɵUnwrapInputSignalWriteType<Field> =
|
||||
Field extends InputSignalWithTransform<any, infer WriteT>? WriteT : never;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue