microsoft / microsoft/TypeScript

Confusing in narrowing and property access on type parameter with `never` base constraint

Open
#62,178 1 comment 0 reactions 0 assignees View on GitHub
Domain: check: Control Flow Help Wanted Possible Improvement
Dominant language
Go
Stars
111k
Forks
14.3k
Avg merge
2d 4h
Merged PRs (30d)
132

Description

### 🔎 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

```ts
declare class ZodType {
_output: T;
}

declare class ZodObject extends ZodType {
_shape: Shape;
}

type inferType> = T["_output"];

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

type ToolParameters = Parameters[0];

export type AgentToolDefaultSchema = ZodObject;

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

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

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

type AgentToolDescription = {
name: string;
description: string;
schema: S;
run: AgentToolCallback;
};

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

export function analysisTool<
T extends ToolSpecifier,
S extends ZodObject>,
>(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

```ts
Property 'point' does not exist on type 'inferType & 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`:
```ts
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

Contributor guide

Open the contributing guide

Research direction

Start by running the linked TypeScript Playground repro and compare the generic narrowing and property-access diagnostics with the standalone `never` examples in the issue. Trace the checker behavior for the `"point" in params` branch; done means the reported behavior is made consistent or the diagnostic clearly explains the underlying type constraint.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.