Skip to content

Confusing in narrowing and property access on type parameter with never base constraintΒ #62178

@Andarist

Description

@Andarist

πŸ”Ž Search Terms

never in record unknown constraint base property access

πŸ•— Version & Regression Information

  • This is the behavior in every version I tried

⏯ Playground Link

https://www.typescriptlang.org/play/?ts=6.0.0-dev.20250802#code/CYUwxgNghgTiAEkoGdnwFoHtgBUCeADiADw4B88A3gFDzwD6mArgC4GsBc8OA3NQL7VqoJHETRUGbAHkARgCtwLYgGUAFlCIAaeNNbsWFEAA8WIAHbA0WXIRJ62rCjTr1kGol3WaQfQdRY7eABLcwAzEBh8IlJ4EzNLa2xokihzPDIKAF5uAG0AIkZ9VnyAXT5qULMYMKgwBBxMTAgVIjBgsODIqlp4cygAWxAuZBYYUIBzPjpQZDBxghZgzHMRscnp+BgmVfgACgJYQeQucxAAN0iASngsigAFGEwB4OQSUfHzCbI-IUCibhNCD3I5DarIWLxCxWQHNVrgDpdGDZeAgmCDEDg0gFbbmMpkXIABnKQhMBEwMBY8H+CAAghMLCxGs0ACIgWpMCAsFRgNQgAZQW5SYByRRgZRpPA6SU-UnGcmUkLmaq1erwemM5kQABCKBIvRUcVM0LQGuVWrZHK5PL5AqFZqZQMtUE53N5-KgWmozl6-SGa0+U16s3mwUWy12Hw2vVCBhtHq8vwCQQdWoAwlAIBBZHUANbEA1GhIw1NO9ku63uu05Uus8uu+MCr0ovawCYnJURKJ2VRkG53VFPF5vYgyipkilUqqRVV0hnmoEFuiGqGJdXzx11q1u22Cmsbi31yu75tFk3rzVA3UjlQ+ui4ri1iAZrM5sD529JmkXhd1uYLJYVlUM81yfZ0GyrKAURcPoMQDaMZhAf8w0AyN1i+TY5l3RNegfH9N2fTNszzXs-AqERoDEMIdnFCN4CgA9F0LVcS0YrcKx3D17TYiBwOPD1mz2ShYKGHQQwAiMdCwgSth2eB+EfHi2WQ8MgNvK5FMvZpSLlBUqWo8xaJWej+ggPBkFeLUl24ECYS1eF2k6SIvWXWykhFBQlFHdIdC1NEMSxcgyEElguBwKTIK8G4YLAFZRioESQDEpDQ1U8x5KFFhNjgFgmBgdKGK0iAhN6Og-SS0r4HElDJMq6Sm0qvCUDwQz9kOdEBmQfs7zoXqOn2fJySqfIlXgdrjngAAySbqTsTAwjG0FkAAOiG5VbiyHJ8ijL58miyresWjq0ByGDDsO5bLvGzqXPOuh+E2Q7BDunK8oKgB3KBgipFhltxA4lquR75Nu-ggYEIA

πŸ’» Code

declare class ZodType<T> {
  _output: T;
}

declare class ZodObject<Shape, Output> extends ZodType<Output> {
  _shape: Shape;
}

type inferType<T extends ZodType<any>> = T["_output"];

interface ToolSpecifier {
  name: string;
  description: string;
  run: (params: never) => Promise<string>;
}

type ToolParameters<T extends ToolSpecifier> = Parameters<T["run"]>[0];

export type AgentToolDefaultSchema = ZodObject<any, any>;

export interface AgentToolBase<
  S extends AgentToolDefaultSchema = AgentToolDefaultSchema,
> {
  name: string;
  description: string;
  inputSchema: S;
}

type AgentToolCallback<
  S extends AgentToolDefaultSchema = AgentToolDefaultSchema,
> = (args: inferType<S>) => Promise<any>;

export interface AgentTool<
  S extends AgentToolDefaultSchema = AgentToolDefaultSchema,
> extends AgentToolBase<S> {
  run: AgentToolCallback<S>;
}

type AgentToolDescription<S extends AgentToolDefaultSchema> = {
  name: string;
  description: string;
  schema: S;
  run: AgentToolCallback<S>;
};

declare function agentTool<
  S extends AgentToolDefaultSchema = AgentToolDefaultSchema,
>({ name, description, schema, run }: AgentToolDescription<S>): AgentTool<S>;

export function analysisTool<
  T extends ToolSpecifier,
  S extends ZodObject<any, ToolParameters<T>>,
>(t: T, schema: S) {
  const { name, description } = t;
  return agentTool({
    name,
    description,
    schema,
    run: async (params) => {
      if ("point" in params && typeof params.point === "string") {
        params = {
          ...params,
        };
      }
      return await t.run(params);
    },
  });
}

πŸ™ Actual behavior

Property 'point' does not exist on type 'inferType<S> & Record<"point", unknown>'.(2339)

πŸ™‚ Expected behavior

This should be allowed or a better error should be reported

Additional information about the issue

This stems from inconsistent treatment of never:

declare const test: never;

if ("foo" in test) { // ok
}

test.foo; // error
test["foo"]; // ok
const { foo } = test; // ok

but given this whole thing happens at generic level it's not even apparent to the user they deal with a type parameter that has a never base constraint and that this might be the reason behind this confusing error

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions