The last overload gave the following error. Type 'unknown' is not assignable to type 'Foo'
I started to get this error today after updating vscode extension to this version:
Identifier
typescriptteam.native-preview
Version
0.20250821.1
Last Updated
2025-08-21, 09:18:07
No overload matches this call.
The last overload gave the following error.
Argument of type 'DialogConfig<IConfirmationModal<NeighborOut>, DialogRef<NeighborOut, ModalConfirmation<NeighborOut>>, BasePortalOutlet>' is not assignable to parameter of type 'DialogConfig<IConfirmationModal<NeighborOut>, DialogRef<NeighborOut, ModalConfirmation<unknown>>, BasePortalOutlet>'.
Types of property 'providers' are incompatible.
Type 'StaticProvider[] | ((dialogRef: DialogRef<NeighborOut, ModalConfirmation<NeighborOut>>, config: DialogConfig<IConfirmationModal<...>, DialogRef<...>, BasePortalOutlet>, container: BasePortalOutlet) => StaticProvider[]) | undefined' is not assignable to type 'StaticProvider[] | ((dialogRef: DialogRef<NeighborOut, ModalConfirmation<unknown>>, config: DialogConfig<IConfirmationModal<NeighborOut>, DialogRef<...>, BasePortalOutlet>, container: BasePortalOutlet) => StaticProvider[]) | undefined'.
Type '(dialogRef: DialogRef<NeighborOut, ModalConfirmation<NeighborOut>>, config: DialogConfig<IConfirmationModal<NeighborOut>, DialogRef<...>, BasePortalOutlet>, container: BasePortalOutlet) => StaticProvider[]' is not assignable to type 'StaticProvider[] | ((dialogRef: DialogRef<NeighborOut, ModalConfirmation<unknown>>, config: DialogConfig<IConfirmationModal<NeighborOut>, DialogRef<...>, BasePortalOutlet>, container: BasePortalOutlet) => StaticProvider[]) | undefined'.
Type '(dialogRef: DialogRef<NeighborOut, ModalConfirmation<NeighborOut>>, config: DialogConfig<IConfirmationModal<NeighborOut>, DialogRef<...>, BasePortalOutlet>, container: BasePortalOutlet) => StaticProvider[]' is not assignable to type 'StaticProvider[] | ((dialogRef: DialogRef<NeighborOut, ModalConfirmation<unknown>>, config: DialogConfig<IConfirmationModal<NeighborOut>, DialogRef<...>, BasePortalOutlet>, container: BasePortalOutlet) => StaticProvider[])'.
Type '(dialogRef: DialogRef<NeighborOut, ModalConfirmation<NeighborOut>>, config: DialogConfig<IConfirmationModal<NeighborOut>, DialogRef<...>, BasePortalOutlet>, container: BasePortalOutlet) => StaticProvider[]' is not assignable to type '(dialogRef: DialogRef<NeighborOut, ModalConfirmation<unknown>>, config: DialogConfig<IConfirmationModal<NeighborOut>, DialogRef<...>, BasePortalOutlet>, container: BasePortalOutlet) => StaticProvider[]'.
Types of parameters 'dialogRef' and 'dialogRef' are incompatible.
Type 'DialogRef<NeighborOut, ModalConfirmation<unknown>>' is not assignable to type 'DialogRef<NeighborOut, ModalConfirmation<NeighborOut>>'.
The types of 'config.providers' are incompatible between these types.
Type 'StaticProvider[] | ((dialogRef: DialogRef<NeighborOut, ModalConfirmation<unknown>>, config: DialogConfig<any, DialogRef<NeighborOut, ModalConfirmation<...>>, DialogContainer>, container: DialogContainer) => StaticProvider[]) | undefined' is not assignable to type 'StaticProvider[] | ((dialogRef: DialogRef<NeighborOut, ModalConfirmation<NeighborOut>>, config: DialogConfig<any, DialogRef<...>, DialogContainer>, container: DialogContainer) => StaticProvider[]) | undefined'.
Type '(dialogRef: DialogRef<NeighborOut, ModalConfirmation<unknown>>, config: DialogConfig<any, DialogRef<NeighborOut, ModalConfirmation<unknown>>, DialogContainer>, container: DialogContainer) => StaticProvider[]' is not assignable to type 'StaticProvider[] | ((dialogRef: DialogRef<NeighborOut, ModalConfirmation<NeighborOut>>, config: DialogConfig<any, DialogRef<...>, DialogContainer>, container: DialogContainer) => StaticProvider[]) | undefined'.
Type '(dialogRef: DialogRef<NeighborOut, ModalConfirmation<unknown>>, config: DialogConfig<any, DialogRef<NeighborOut, ModalConfirmation<unknown>>, DialogContainer>, container: DialogContainer) => StaticProvider[]' is not assignable to type 'StaticProvider[] | ((dialogRef: DialogRef<NeighborOut, ModalConfirmation<NeighborOut>>, config: DialogConfig<any, DialogRef<...>, DialogContainer>, container: DialogContainer) => StaticProvider[])'.
Type '(dialogRef: DialogRef<NeighborOut, ModalConfirmation<unknown>>, config: DialogConfig<any, DialogRef<NeighborOut, ModalConfirmation<unknown>>, DialogContainer>, container: DialogContainer) => StaticProvider[]' is not assignable to type '(dialogRef: DialogRef<NeighborOut, ModalConfirmation<NeighborOut>>, config: DialogConfig<any, DialogRef<NeighborOut, ModalConfirmation<NeighborOut>>, DialogContainer>, container: DialogContainer) => StaticProvider[]'.
Types of parameters 'dialogRef' and 'dialogRef' are incompatible.
Type 'DialogRef<NeighborOut, ModalConfirmation<NeighborOut>>' is not assignable to type 'DialogRef<NeighborOut, ModalConfirmation<unknown>>'.
Type 'ModalConfirmation<unknown>' is not assignable to type 'ModalConfirmation<NeighborOut>'.
Type 'unknown' is not assignable to type 'NeighborOut'.ts(2769)
The methods the error above is referencing are from "@angular/cdk": "20.2.0"
/**
* Opens a modal dialog containing the given component.
* @param component Type of the component to load into the dialog.
* @param config Extra configuration options.
* @returns Reference to the newly-opened dialog.
*/
open<R = unknown, D = unknown, C = unknown>(component: ComponentType<C>, config?: DialogConfig<D, DialogRef<R, C>>): DialogRef<R, C>;
/**
* Opens a modal dialog containing the given template.
* @param template TemplateRef to instantiate as the dialog content.
* @param config Extra configuration options.
* @returns Reference to the newly-opened dialog.
*/
open<R = unknown, D = unknown, C = unknown>(template: TemplateRef<C>, config?: DialogConfig<D, DialogRef<R, C>>): DialogRef<R, C>;
open<R = unknown, D = unknown, C = unknown>(componentOrTemplateRef: ComponentType<C> | TemplateRef<C>, config?: DialogConfig<D, DialogRef<R, C>>): DialogRef<R, C>;
Do you have the rest of the code that triggers this difference? (Probably, this is #1603?)
//Custom Code
openDeleteNeighborModal(
neighbor: NeighborOut,
body: TemplateRef<void>
): void {
const dialogConfig = getDefaultConfirmationDialogConfig<NeighborOut>({
width: '600px',
title: 'Delete User',
body,
hasActionButton: true,
actionButtonText: 'delete User',
actionButtonSeverity: 'danger',
dismissButtonText: 'Dismiss',
dataSubset: neighbor
});
const deleteUserConfirmationModalRef = this.#dialog.open(
ModalConfirmation,
dialogConfig // <<< This has the type error red underline
);
deleteUserConfirmationModalRef.closed.subscribe(s => {
if (s) this.#confirmDeleteNeighbor(s.id);
});
}
// Custom code
export const getDefaultConfirmationDialogConfig = <TDataSubset>({
disableClose = true,
maximizable = false,
hideCloseX = false,
width,
title,
body,
hasActionButton,
actionButtonText,
actionButtonSeverity,
dismissButtonText,
dismissButtonSeverity,
dataSubset
}: {
disableClose?: boolean;
maximizable?: boolean;
hideCloseX?: boolean;
width?: string;
title: string;
body: TemplateRef<void>;
hasActionButton: boolean;
actionButtonText?: string;
actionButtonSeverity?: Severity;
dismissButtonText: string;
dismissButtonSeverity?: Severity;
dataSubset?: TDataSubset;
}): DialogConfig<
IConfirmationModal<TDataSubset>,
DialogRef<TDataSubset, ModalConfirmation<TDataSubset>>
> => {
return {
disableClose,
width,
data: {
title,
maximizable,
hideCloseX,
body,
hasActionButton,
actionButtonText,
actionButtonSeverity,
dismissButtonText,
dismissButtonSeverity,
subset: dataSubset
}
};
};
// From Angular CDK
/**
* Reference to a dialog opened via the Dialog service.
*/
declare class DialogRef<R = unknown, C = unknown> {
readonly overlayRef: OverlayRef;
readonly config: DialogConfig<any, DialogRef<R, C>, DialogContainer>;
/**
* Instance of component opened into the dialog. Will be
* null when the dialog is opened using a `TemplateRef`.
*/
readonly componentInstance: C | null;
/**
* `ComponentRef` of the component opened into the dialog. Will be
* null when the dialog is opened using a `TemplateRef`.
*/
readonly componentRef: ComponentRef<C> | null;
/** Instance of the container that is rendering out the dialog content. */
readonly containerInstance: DialogContainer;
/** Whether the user is allowed to close the dialog. */
disableClose: boolean | undefined;
/** Emits when the dialog has been closed. */
readonly closed: Observable<R | undefined>;
/** Emits when the backdrop of the dialog is clicked. */
readonly backdropClick: Observable<MouseEvent>;
/** Emits when on keyboard events within the dialog. */
readonly keydownEvents: Observable<KeyboardEvent>;
/** Emits on pointer events that happen outside of the dialog. */
readonly outsidePointerEvents: Observable<MouseEvent>;
/** Unique ID for the dialog. */
readonly id: string;
/** Subscription to external detachments of the dialog. */
private _detachSubscription;
constructor(overlayRef: OverlayRef, config: DialogConfig<any, DialogRef<R, C>, DialogContainer>);
/**
* Close the dialog.
* @param result Optional result to return to the dialog opener.
* @param options Additional options to customize the closing behavior.
*/
close(result?: R, options?: DialogCloseOptions): void;
/** Updates the position of the dialog based on the current position strategy. */
updatePosition(): this;
/**
* Updates the dialog's width and height.
* @param width New width of the dialog.
* @param height New height of the dialog.
*/
updateSize(width?: string | number, height?: string | number): this;
/** Add a CSS class or an array of classes to the overlay pane. */
addPanelClass(classes: string | string[]): this;
/** Remove a CSS class or an array of classes from the overlay pane. */
removePanelClass(classes: string | string[]): this;
/** Whether the dialog is allowed to close. */
private _canClose;
}
// Custom code
export interface IConfirmationModal<TSubSet> {
title: string;
maximizable: boolean;
hideCloseX: boolean;
body: TemplateRef<void>;
hasActionButton: boolean;
actionButtonText?: string;
actionButtonSeverity?: Severity;
dismissButtonText: string;
dismissButtonSeverity?: Severity;
subset?: TSubSet;
}
// From Angular CDK
/** Configuration for opening a modal dialog. */
declare class DialogConfig<D = unknown, R = unknown, C extends DialogContainer = BasePortalOutlet> {
/**
* Where the attached component should live in Angular's *logical* component tree.
* This affects what is available for injection and the change detection order for the
* component instantiated inside of the dialog. This does not affect where the dialog
* content will be rendered.
*/
viewContainerRef?: ViewContainerRef;
/**
* Injector used for the instantiation of the component to be attached. If provided,
* takes precedence over the injector indirectly provided by `ViewContainerRef`.
*/
injector?: Injector;
/** ID for the dialog. If omitted, a unique one will be generated. */
id?: string;
/** The ARIA role of the dialog element. */
role?: DialogRole;
/** Optional CSS class or classes applied to the overlay panel. */
panelClass?: string | string[];
/** Whether the dialog has a backdrop. */
hasBackdrop?: boolean;
/** Optional CSS class or classes applied to the overlay backdrop. */
backdropClass?: string | string[];
/** Whether the dialog closes with the escape key or pointer events outside the panel element. */
disableClose?: boolean;
/** Function used to determine whether the dialog is allowed to close. */
closePredicate?: <Result = unknown, Component = unknown, Config extends DialogConfig = DialogConfig>(result: Result | undefined, config: Config, componentInstance: Component | null) => boolean;
/** Width of the dialog. */
width?: string;
/** Height of the dialog. */
height?: string;
/** Min-width of the dialog. If a number is provided, assumes pixel units. */
minWidth?: number | string;
/** Min-height of the dialog. If a number is provided, assumes pixel units. */
minHeight?: number | string;
/** Max-width of the dialog. If a number is provided, assumes pixel units. */
maxWidth?: number | string;
/** Max-height of the dialog. If a number is provided, assumes pixel units. */
maxHeight?: number | string;
/** Strategy to use when positioning the dialog. Defaults to centering it on the page. */
positionStrategy?: PositionStrategy;
/** Data being injected into the child component. */
data?: D | null;
/** Layout direction for the dialog's content. */
direction?: Direction;
/** ID of the element that describes the dialog. */
ariaDescribedBy?: string | null;
/** ID of the element that labels the dialog. */
ariaLabelledBy?: string | null;
/** Dialog label applied via `aria-label` */
ariaLabel?: string | null;
/**
* Whether this is a modal dialog. Used to set the `aria-modal` attribute. Off by default,
* because it can interfere with other overlay-based components (e.g. `mat-select`) and because
* it is redundant since the dialog marks all outside content as `aria-hidden` anyway.
*/
ariaModal?: boolean;
/**
* Where the dialog should focus on open.
* @breaking-change 14.0.0 Remove boolean option from autoFocus. Use string or
* AutoFocusTarget instead.
*/
autoFocus?: AutoFocusTarget | string | boolean;
/**
* Whether the dialog should restore focus to the previously-focused element upon closing.
* Has the following behavior based on the type that is passed in:
* - `boolean` - when true, will return focus to the element that was focused before the dialog
* was opened, otherwise won't restore focus at all.
* - `string` - focus will be restored to the first element that matches the CSS selector.
* - `HTMLElement` - focus will be restored to the specific element.
*/
restoreFocus?: boolean | string | HTMLElement;
/**
* Scroll strategy to be used for the dialog. This determines how
* the dialog responds to scrolling underneath the panel element.
*/
scrollStrategy?: ScrollStrategy;
/**
* Whether the dialog should close when the user navigates backwards or forwards through browser
* history. This does not apply to navigation via anchor element unless using URL-hash based
* routing (`HashLocationStrategy` in the Angular router).
*/
closeOnNavigation?: boolean;
/**
* Whether the dialog should close when the dialog service is destroyed. This is useful if
* another service is wrapping the dialog and is managing the destruction instead.
*/
closeOnDestroy?: boolean;
/**
* Whether the dialog should close when the underlying overlay is detached. This is useful if
* another service is wrapping the dialog and is managing the destruction instead. E.g. an
* external detachment can happen as a result of a scroll strategy triggering it or when the
* browser location changes.
*/
closeOnOverlayDetachments?: boolean;
/**
* Whether the built-in overlay animations should be disabled.
*/
disableAnimations?: boolean;
/**
* Providers that will be exposed to the contents of the dialog. Can also
* be provided as a function in order to generate the providers lazily.
*/
providers?: StaticProvider[] | ((dialogRef: R, config: DialogConfig<D, R, C>, container: C) => StaticProvider[]);
/**
* Component into which the dialog content will be rendered. Defaults to `CdkDialogContainer`.
* A configuration object can be passed in to customize the providers that will be exposed
* to the dialog container.
*/
container?: Type<C> | {
type: Type<C>;
providers: (config: DialogConfig<D, R, C>) => StaticProvider[];
};
/**
* Context that will be passed to template-based dialogs.
* A function can be passed in to resolve the context lazily.
*/
templateContext?: Record<string, any> | (() => Record<string, any>);
}
Let me know if I missed any relevant types, thanks!
I noticed #1603 has already been merged, so I checked the newest version of the extension in VSCode but still the issue is present.
Version 0.20251124.1
~~I think this could be closed now! I just updated to version 0.20251127.1 and the error is no longer there. Thank u~~
Do you know what version between those fixed it? Not sure we changed something on this front
@jakebailey oh you are absolutely right, I updated the extensions but didn't realize it was disabled. sorry about the confusion.
So, it's fixed? 😄
not yet, sorry
We need minimal isolated repro.
not a problem, I'll put something together and let you know
Here is the repro. I added some comments, but let me know if any question, thank u
https://github.com/urielzen/tsgoissue1616/blob/main/1616.ts
This is a type ordering thing; TS7 has slightly different ordering rules. In TS6 you get the same behavior (any, any) by swapping this union order:
type ProviderToken<T> = AbstractType<T> | Type$1<T>;
This is pretty unsurprising when you look at the definitions, both of these types basically say "What is the instance type of this constructor"
interface Type$1<T> extends Function {
new (...args: any[]): T;
}
interface AbstractType<T> extends Function {
prototype: T;
}
While Type$1 gets the constructor return type (thus is eligible for generic inference), AbstractType is just seeing the fixed instantiation we put in place for prototype. Both inferences seem valid so we proceed accordingly and pick the "first" encountered, but TS7 has a more-consistent interpretation of "first" and thus is always picking the prototype one because, in the absence of any other tiebreaker, A alphabetically precedes T.
What you really have here is a situation where Type$1 is the unambiguously better inference source, and would probably be a lot better off having overloads:
declare function inject<T>(token: Type$1<T>): T;
declare function inject<T>(token: AbstractType<T>): T;
cc @ahejlsberg in case he can spot a tiebreaker here but I don't see one
Minimal example that doesn't have any potential red herrings:
class ModalConfirmation<TSubSet> {
readonly #dialogRef = inject(DialogRef<TSubSet>);
foo() {
const m = this.#dialogRef;
// ^?
}
}
// mock Angular core
interface AbstractType<T> extends Function {
prototype: T;
}
interface Type$1<T> extends Function {
new (...args: any[]): T;
}
type ProviderToken<T> = Type$1<T> | AbstractType<T>;
//type ProviderToken<T> = AbstractType<T> | Type$1<T>;
declare function inject<T>(token: ProviderToken<T>): T;
declare class DialogConfig<D = unknown, R = unknown> {
providers: (dialogRef: D) => unknown;
self: R;
}
declare class DialogRef<R = unknown> {
readonly config: DialogConfig<any, DialogRef<R>>;
}