Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jsonify fails on nested object with name property. #657

Closed
RalphSleigh opened this issue Jul 29, 2023 · 15 comments · Fixed by #691
Closed

Jsonify fails on nested object with name property. #657

RalphSleigh opened this issue Jul 29, 2023 · 15 comments · Fixed by #691

Comments

@RalphSleigh
Copy link

RalphSleigh commented Jul 29, 2023

Code here:
https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgKQM4QHbAGYE84C+c2UEIcARDLmAKYC02tqMFA3AFAfV1wQBGAKwByzGLQAmwgIYhacALyIOcVcWBQWALmVq9cDLNo6WUYBgDmKtQQ63uNeQJFjJwiDLmLda7Bu0++gYQMJ7GcKbmVnq29jzygugYAOrAMAAWYd5omDi4ADzOoixuRgB8DryJmADyQsXiUh5G2Ul5hfWuTWFlQA

import { Jsonify } from "type-fest";

type objNestedName = {
    first: {
        name: string
    }
}

type objNestedNoName = {
    first: {
        notName: string
    }
}

type jsonWithName = Jsonify<objNestedName>
type jsonObjNestedNoName = Jsonify<objNestedNoName>

Note if you mouseover the second one you can see inside the object, but the first one is an empty object. Might be wrong but I am fairly sure name is not a reserved word in typescript or ES6 so would expect this to work.

Upvote & Fund

  • We're using Polar.sh so you can upvote and help fund this issue.
  • The funding will be given to active contributors.
  • Thank you in advance for helping prioritize & fund our backlog.
Fund with Polar
@sindresorhus
Copy link
Owner

When I mouse over them in the playground, they both show the same thing.

@RalphSleigh
Copy link
Author

RalphSleigh commented Aug 1, 2023

Playground

Yeah, output has changed since I posted the issue. Not sure why there, maybe due to the release? The mouseover now displays both as:

type jsonWithName = {
    [x: string]: number;
}

Which is not right either, but the structured types plugin gets it right if you enable/run that.

Locally

I updated to the latest version of type-fest locally and can still see the issue in visual studio code:

Screenshot 2023-08-01 115753
Screenshot 2023-08-01 115747

This is probably something to do with my environment, I just have no idea what.

@RalphSleigh
Copy link
Author

Trying to narrow this down, I made a new folder and added that example, Node is: 17.8

npm install typescript type-fest

This gets me typescript 5.1.6 and I can see the issue

npm install [email protected]

No issue

npm install [email protected]

Its back.

@donovanhiland
Copy link
Contributor

donovanhiland commented Aug 14, 2023

@sindresorhus @RalphSleigh I'm experiencing the same thing today, dug into it a bit and found this. BaseKeyFilter is used in Jsonify:

import { Jsonify } from "type-fest";

type objNestedName = {
    first: {
        name: string
    }
}

type objNestedNoName = {
    first: {
        notName: string
    }
}

type jsonWithName = Jsonify<objNestedName>
type jsonObjNestedNoName = Jsonify<objNestedNoName>

type BaseKeyFilter<Type, Key extends keyof Type> = Key extends symbol
	? never
	: Type[Key] extends symbol
		? never
		: [(...arguments_: any[]) => any] extends [Type[Key]] // <------- I think is where the issue lies
			? 'Uh oh' // <------- Which lands us here (it's `never` actually but you get the idea)
			: Key;

type Test1 = BaseKeyFilter<objNestedName, 'first'> // 'Uh oh'
type Test2 = BaseKeyFilter<objNestedNoName, 'first'> // 'first'

type SomeObjWithNameProp = { name: string }
type SomeFunction = (...args: any[]) => any

type Test3 = [SomeFunction] extends [SomeObjWithNameProp] ? 'Uh oh' : 'Yay' // 'Uh oh'

type Test4 = SomeFunction extends Record<string, unknown> ? 'Uh oh' : 'Yay' // 'Yay'
type Test5 = SomeObjWithNameProp extends Record<string, unknown> ? 'Yay' : 'Uh oh' // 'Yay'

type FixedBaseKeyFilter<Type, Key extends keyof Type> = Key extends symbol
	? never
	: Type[Key] extends symbol
		? never
        : Type[Key] extends Record<string, unknown>  // <------- If we check for record first this helps
            ? Key
		    : [(...arguments_: any[]) => any] extends [Type[Key]]
			    ? never
			    : Key;

type Test6 = FixedBaseKeyFilter<objNestedName, 'first'> // 'first'
type Test7 = FixedBaseKeyFilter<objNestedNoName, 'first'> // 'first'

It seems function extends { name: 'anything' }, which makes sense I guess but is kind of a gotcha. Function does not extend { name: 'anything', someOtherProp: 'anything' }.

Playground link

@sindresorhus
Copy link
Owner

It seems d83d62c is the cause then.

@RalphSleigh
Copy link
Author

@donovanhiland Thanks, appears for now I can work around this by adding another property.

@ThomasBoxall Cool huh?

@sindresorhus
Copy link
Owner

@donovanhiland Thanks for looking into this. Would you be willing to submit a pull request?

@donovanhiland
Copy link
Contributor

@sindresorhus Sure I can probably put something up this week

@nmackey
Copy link

nmackey commented Sep 20, 2023

Any status updates on this?

@duvet86
Copy link

duvet86 commented Sep 21, 2023

After a quick investigation I found that "UndefinedToOptional" is causing the issue. Removing it allows all the properties to be visible again.

@duvet86
Copy link

duvet86 commented Sep 24, 2023

Something to do with:

type BaseKeyFilter<Type, Key extends keyof Type> = Key extends symbol
	? never
	: Type[Key] extends symbol
		? never
		: [(...arguments_: any[]) => any] extends [Type[Key]] // HERE. Remove me and the name property will reappear.
			? never
			: Key;

I tried several things but nothing works. In my opinion it is caused by the "name" property of a function. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name

I can't find a solution, I hope it helps someone smarter.
Cheeers.

@milamer
Copy link

milamer commented Sep 25, 2023

I fixed this via a patch for me. Not sure if other problems arise with this (version 4.3.1)

diff --git a/node_modules/type-fest/source/internal.d.ts b/node_modules/type-fest/source/internal.d.ts
index ea479aa..64cb298 100644
--- a/node_modules/type-fest/source/internal.d.ts
+++ b/node_modules/type-fest/source/internal.d.ts
@@ -189,7 +189,7 @@ type BaseKeyFilter<Type, Key extends keyof Type> = Key extends symbol
 	? never
 	: Type[Key] extends symbol
 		? never
-		: [(...arguments_: any[]) => any] extends [Type[Key]]
+		:   [Type[Key]] extends [((...arguments_: any[]) => any)] 
 			? never
 			: Key;
 

@duvet86
Copy link

duvet86 commented Sep 25, 2023

That solves the issue but breaks existing unit tests.

@milamer
Copy link

milamer commented Sep 28, 2023

@donovanhiland

Thanks for the quick fix, but I still have this error with a type like this

type Test1 = {foo: { name: string } | null }
type Test2 = {foo: { name: string } | [] }

I changed the type to this

type BaseKeyFilter<Type, Key extends keyof Type> = Key extends symbol
	? never
	: Type[Key] extends symbol
		? never
-		: Type[Key] extends  Record<string, unknown>  
+		: {name: string} extends Type[Key]
			? Key
			: [(...arguments_: any[]) => any] extends [Type[Key]]
				? never
				: Key;

and now it seems to work with null values

type Test = BaseKeyFilter<{a: {name: string} | null}, 'a'>

type Test = "a"

But I am not sure how this type should be handled

type TestFunction = BaseKeyFilter<{a: () => {} | null}, 'a'>

type TestFunction = never

@milamer
Copy link

milamer commented Sep 28, 2023

Also added these tests

declare const nestedObjectWithNamePropertyNullUnion: {
	first: {
		name: string;
	} | null;
};
declare const jsonifiedNestedObjectWithNamePropertyNullUnion: Jsonify<typeof nestedObjectWithNamePropertyNullUnion>;

expectType<typeof nestedObjectWithNamePropertyNullUnion>(jsonifiedNestedObjectWithNamePropertyNullUnion);

declare const nestedObjectWithNamePropertyArrayUnion: {
	first: {
		name: string;
	} | [];
};
declare const jsonifiedNestedObjectWithNamePropertyArrayUnion: Jsonify<typeof nestedObjectWithNamePropertyArrayUnion>;

expectType<typeof nestedObjectWithNamePropertyArrayUnion>(jsonifiedNestedObjectWithNamePropertyArrayUnion);

and all tests work on my end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
6 participants