Skip to content

Commit

Permalink
refactor(core): separate InputSignal and InputSignalWithTransform (
Browse files Browse the repository at this point in the history
…#54233)

This commit separates `InputSignal` for input signals with transforms.
The reason being that most of the time, signal inputs are not using
transforms and the generics are rather confusing.

Especially for users with inferred types displayed in their IDEs, the
input signal types are seemingly complex, even if no transform is used.

For this reason, we are introducing a new type called
`InputSignalWithTransform`. This type will be used for inputs with
transforms, while non-transform inputs just use `InputSignal`.

A notable fact is that `InputSignal` extends `InputSignalWithTransform`,
with the "identity transform". i.e. there is no transform. This allows
us to share the code for input signals. In practice, we don't expect
users to pass around `InputSignal`'s anyway.

PR Close #54233
  • Loading branch information
devversion authored and thePunderWoman committed Feb 5, 2024
1 parent ea2fd1b commit a42f0ba
Show file tree
Hide file tree
Showing 13 changed files with 169 additions and 152 deletions.
10 changes: 7 additions & 3 deletions goldens/public-api/core/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -883,10 +883,10 @@ export interface InputFunction {
// (undocumented)
<ReadT>(initialValue: ReadT, opts?: InputOptionsWithoutTransform<ReadT>): InputSignal<ReadT>;
// (undocumented)
<ReadT, WriteT>(initialValue: ReadT, opts: InputOptionsWithTransform<ReadT, WriteT>): InputSignal<ReadT, WriteT>;
<ReadT, WriteT>(initialValue: ReadT, opts: InputOptionsWithTransform<ReadT, WriteT>): InputSignalWithTransform<ReadT, WriteT>;
required: {
<ReadT>(opts?: InputOptionsWithoutTransform<ReadT>): InputSignal<ReadT>;
<ReadT, WriteT>(opts: InputOptionsWithTransform<ReadT, WriteT>): InputSignal<ReadT, WriteT>;
<ReadT, WriteT>(opts: InputOptionsWithTransform<ReadT, WriteT>): InputSignalWithTransform<ReadT, WriteT>;
};
}

Expand All @@ -905,7 +905,11 @@ export type InputOptionsWithoutTransform<ReadT> = Omit<InputOptions<ReadT, ReadT
export type InputOptionsWithTransform<ReadT, WriteT> = Required<Pick<InputOptions<ReadT, WriteT>, 'transform'>> & InputOptions<ReadT, WriteT>;

// @public
export interface InputSignal<ReadT, WriteT = ReadT> extends Signal<ReadT> {
export interface InputSignal<ReadT> extends InputSignalWithTransform<ReadT, ReadT> {
}

// @public
export interface InputSignalWithTransform<ReadT, WriteT> extends Signal<ReadT> {
// (undocumented)
INPUT_SIGNAL_BRAND_READ_TYPE]: ReadT;
// (undocumented)
Expand Down
39 changes: 13 additions & 26 deletions packages/compiler-cli/src/ngtsc/testing/fake_core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,41 +141,28 @@ export type InputOptionsWithTransform<ReadT, WriteT> =
const ɵINPUT_SIGNAL_BRAND_READ_TYPE = /* @__PURE__ */ Symbol();
export const ɵINPUT_SIGNAL_BRAND_WRITE_TYPE = /* @__PURE__ */ Symbol();

export interface InputSignal<ReadT, WriteT = ReadT> extends Signal<ReadT> {
export interface InputSignalWithTransform<ReadT, WriteT> extends Signal<ReadT> {
[ɵINPUT_SIGNAL_BRAND_READ_TYPE]: ReadT;
[ɵINPUT_SIGNAL_BRAND_WRITE_TYPE]: WriteT;
}
export interface InputSignal<ReadT> extends InputSignalWithTransform<ReadT, ReadT> {}

export function inputFunction<ReadT>(): InputSignal<ReadT|undefined>;
export function inputFunction<ReadT>(
initialValue: ReadT, opts?: InputOptionsWithoutTransform<ReadT>): InputSignal<ReadT>;
export function inputFunction<ReadT, WriteT>(
initialValue: ReadT,
opts: InputOptionsWithTransform<ReadT, WriteT>): InputSignal<ReadT, WriteT>;
export function inputFunction<ReadT, WriteT>(
_initialValue?: ReadT,
_opts?: InputOptions<ReadT, WriteT>): InputSignal<ReadT|undefined, WriteT> {
return null!;
}
export interface InputFunction {
<ReadT>(): InputSignal<ReadT|undefined>;
<ReadT>(initialValue: ReadT, opts?: InputOptionsWithoutTransform<ReadT>): InputSignal<ReadT>;
<ReadT, WriteT>(initialValue: ReadT, opts: InputOptionsWithTransform<ReadT, WriteT>):
InputSignalWithTransform<ReadT, WriteT>;

export function inputRequiredFunction<ReadT>(opts?: InputOptionsWithoutTransform<ReadT>):
InputSignal<ReadT>;
export function inputRequiredFunction<ReadT, WriteT>(
opts: InputOptionsWithTransform<ReadT, WriteT>): InputSignal<ReadT, WriteT>;
export function inputRequiredFunction<ReadT, WriteT>(_opts?: InputOptions<ReadT, WriteT>):
InputSignal<ReadT, WriteT> {
return null!;
required: {
<ReadT>(opts?: InputOptionsWithoutTransform<ReadT>): InputSignal<ReadT>;<ReadT, WriteT>(
opts: InputOptionsWithTransform<ReadT, WriteT>): InputSignalWithTransform<ReadT, WriteT>;
};
}

export type InputFunction = typeof inputFunction&{required: typeof inputRequiredFunction};

export const input: InputFunction = (() => {
(inputFunction as any).required = inputRequiredFunction;
return inputFunction as InputFunction;
})();
export const input: InputFunction = null!;

export type ɵUnwrapInputSignalWriteType<Field> =
Field extends InputSignal<unknown, infer WriteT>? WriteT : never;
Field extends InputSignalWithTransform<unknown, infer WriteT>? WriteT : never;
export type ɵUnwrapDirectiveSignalInputs<Dir, Fields extends keyof Dir> = {
[P in Fields]: ɵUnwrapInputSignalWriteType<Dir[P]>
};
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ class InterpolatedSignalCheck extends

function isSignal(symbol: ts.Symbol|undefined): boolean {
return (symbol?.escapedName === 'WritableSignal' || symbol?.escapedName === 'Signal' ||
symbol?.escapedName === 'InputSignal') &&
symbol?.escapedName === 'InputSignal' ||
symbol?.escapedName === 'InputSignalWithTransform') &&
(symbol as any).parent.escapedName.includes('@angular/core');
}

Expand Down

0 comments on commit a42f0ba

Please sign in to comment.