Skip to content

Typescript 5.9 can no longer derive this type, becomes 'any' instead #62256

@FritsvanCampen

Description

@FritsvanCampen

🔎 Search Terms

I define a React component MyComponent with a fairly complex prop-type, then I use the component in Example.

The type issue occurs where the type of the parameter item of setItem can no longer be derived as { id: string } | null, instead it is now derived as any which breaks the .id access.

Is this change expected in 5.9? I don't seen anything in the changelog that would cause me to expect this change.

🕗 Version & Regression Information

  • This changed between versions 5.8.3 and 5.9.2

⏯ Playground Link

https://www.typescriptlang.org/play/?ts=5.9.2#code/JYWwDg9gTgLgBAbzgVwM4FMDKMCGN1wC+cAZlBCHAERTo4DGMVA3AFCswCeYBAkgCboAdjGAlgOAEYAbAgF5EcYPwBccVDCjAhAcyJtW2-FBIMCAUSFTZAQUSsAkLRz8IQ6ZzjDr6G2s3I6GxOdK7unt4y6ABC-lCBzHAA9ElwMAAWwKhw0toEGOgg2aCQsDgirITsRugmZnAAIlk+dgiOzmEeXlZRNgD8aqbSGGxVhiK1pvQWPbLR9iEubl2RcwNp8UGV1RN1043NUfNti50RszGDOMNbYzV7Mz4Awgsdy+fPcQnb48ZTBE1UM9XqF3t1nushiMfugAB6leD3f5wSw+BoAHl4Xlh+CE-GyAmEonEPgAfCCluFwVEGl8tqcwQA3a6BNS8ADaVGUVAAunAAD5wITIaTSYJvKkYGC8fAgNQACmAsrZAqFIukAEo4HJyYyIMpVQAFcggLLodF65Sk0bsOEIpS7ZGAtGY7G4-FwQkiMQSKLkk4SlYXWmka7QhlU5nSVmezncnni0GS9DS5VwRVp3hanVwS38I0ms0W-X8a0-Lg8OAAWU4TwokCERONEDAqFdcPdBME3pJfu16dRvVVzt6WoAZI55YO5sPDnNx5Pp+gXoKR7InguHPK1+gMbxyYKl3vSRqDPQ3Bpq7X624if32zjhB6vcTfbJSfKwORW2oa3XwLeIjNq2mIntq-rtCmyBQEIcDoqSCBfi2qAAHRRoEhDokkZaEGeF7wOYsI4OAsj9vK2YQQ455CJe7JKLKAA06gpjKhRwHyChoFguD4OiGhaLoqrCqKH7CZqBghDA0Gweif43o2FQOA46HoHICBKoUVRKVKrEgGpGaFBRCxKWI6YaSAWonEpDg6bKBkgChyino4SnEOgNzGdpLF2WJznWVpDiEAFqy7mpAToFp2GjGwQA

💻 Code

import { useState } from "react";

type Identifiable = { id: string };

interface EnableA {
	readonly enableA: true;
	readonly enableB: true; // this line seems important
}

interface DisableA {
	readonly enableA?: false;
}

interface EnableB {
	readonly enableB?: true;
}

interface DisableB {
	readonly enableB: false;
}

interface EnableC {
	readonly enableC: true;
}

interface DisableC {
	readonly enableC?: false;
}

export interface EnableD<I extends Identifiable> {
	readonly enableD: true;
	readonly value: I["id"] | null;
	readonly setItem: (item: I | null) => void | Promise<void>;
}

export interface DisableD<I extends Identifiable> {
	readonly enableD: false;
	readonly value: I["id"];
	readonly setItem: (item: I) => void | Promise<void>;
}

type MyComponentProps<I extends Identifiable> = (EnableA | DisableA) &
	(EnableB | DisableB) &
	(EnableC | DisableC) &
	(DisableD<I> | EnableD<I>);

const MyComponent = <I extends Identifiable>(props: MyComponentProps<I>) => {
	return <>{props.value}</>;
};

const Example = () => {
	const [ item, setItem ] = useState<string | null>(null);

	return <MyComponent
		value={item}
		setItem={(item) => { // TS 5.8 would correctly derive `{ id: string } | null` as the type of item, but as of TS 5.9 it's 'any'.
			if (item) {
				setItem(item.id);
			} else {
				setItem(null);
			}
		}}
		enableD={true}
	/>;
};

🙁 Actual behavior

setItem={(item /* type is derived as any */) => {

🙂 Expected behavior

setItem={(item /* type is derived as { id: string } | null */) => {

Additional information about the issue

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Domain: check: Type InferenceRelated to type inference performed during signature resolution or `infer` type resolutionHelp WantedYou can do thisPossible ImprovementThe current behavior isn't wrong, but it's possible to see that it might be better in some cases

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions