#Type that combines two records says argument of type '' is not assignable to parameter of type ''

45 messages · Page 1 of 1 (latest)

unkempt hamlet
#

I have

interface PaginationModel { rowsPerPage: number, page: number } 
type FilteringModel = Record<string, string>
type FilteringAndPaginationModel = Partial<PaginationModel> & FilteringModel
const obj: FilteringAndPaginationModel = { rowsPerPage: 1 }

This causes an issue when trying to use it, TS thinks that this rowsPerPage is supposed to be in the FilteringModel, but it really should be the most specific - PaginationModel. Is there a good way to fix this?

bold perch
#

Partial<PaginationModel> & FilteringModel is an impossible type to satisfy since it has conflicting requirements (edit: okay this isn't strictly true because of the Partial, but it's not possible to specify either rowsPerPage or page). Record<string, string> means that all string keys must have string values, but { rowsPerPage: number, page: number } says that rowsPerPage and page are numbers. both can't be true

#

i'd need more context to offer specific advice here, but potential solutions include moving FilteringModel to its own property:

type FilteringAndPaginationModel = Partial<PaginationModel> & { filtering: FilteringModel }

or widening the value type in FilteringModel to make it allow number values:

type FilteringModel = Record<string, string | number>
type FilteringAndPaginationModel = Partial<PaginationModel> & Partial<FilteringModel>
#

or not using an index signature to begin with 🙂

unkempt hamlet
#

The object is given to axios body, which is given to an asp backend that separates out the two portions of the body. So, nesting it in a { filtering: } doesn't really make sense, since I would need to "unnest" it later anyways. Widening the value isn't ideal, since sometimes direct FilteringModel is used with it expecting to be a string. Sadly, none of these solutions seem ideal.

bold perch
#

IMO it is in general a good idea to uncouple your domain types (what you work with in memory) from serialization formats (like request/response bodies, database rows, etc). in that light i don't see a problem with the former

#

not sure if it makes sense for your use cases, but maybe you don't even need to store these in one obj and can instead keep pagination and filtering as separate values until they need to be packed up into the request body?

#

alternatively, is there any way you could use a more specific type for FilteringModel? i would guess that it's not actually the case that literally any Record<string, string> is valid

unkempt hamlet
#

The C# type is Dictionary<string,string>, so in terms of that, it is valid

bold perch
#

is it meaningful if i pass something silly like {'':'💩'}?

unkempt hamlet
#

Can you give me an example of how you would make the change?

unkempt hamlet
bold perch
#

maybe it could be a generic type on the client? like a FilteringModel<User> differs from a FilteringModel<Automobile>, etc

bold perch
unkempt hamlet
#

I guess the reasoning for making it not very explicit would be that in C# you actually have to declare classes for every little thing. 🤮 You cant just declare an interface type directly where it needs to be used. It needs to have a class.

bold perch
#

yeah, makes sense given C#-isms. but maybe your client code can be stricter since it doesn't have those limitations

#

i'm making a lot of assumptions here though without knowing the specifics of how this stuff is used

unkempt hamlet
#

Would it be possible to do something like
Record<Exclude<string, keyof PaginationModel>, string>
To essentially say that the other object contains everything but those list of keys? Tbh, I don't think it's possible.

#

(Omit)

bold perch
#

no, because string isn't a union type

#

but maybe you can model the positive case (like Partial<Record<keyof User, string>> or whatever the case may be)

#

i guess it's straight up not possible to filter on a property named page given the way the backend works?

unkempt hamlet
#

No, asp likely takes over on that. But I could be wrong.

#

But on the other hand, both properties would be in the body object of the json response, so one would get overwritten regardless

bold perch
#

yeah but it seems like even if i don't want to specify any pagination stuff, i can't do { page: 'foo' } because the backend won't let me, right?

unkempt hamlet
#

(Exclude me, the api uses those values in the query)

bold perch
#

wait, like the query string of the URL?

unkempt hamlet
#

Yeah, so again, it would be overwritten if you had two with the name "page"

bold perch
#

imagine i wrote ?page=foo then 😛

#

that still won't work, even if there's just one thing in there, right?

#

this is pretty lame, but since it sounds like these things are getting encoded as application/x-www-form-urlencoded (standard query string format) that means they're all strings at the end of the day anyway, so you could do this:

old sonnetBOT
#
mkantor#7432

Preview:ts interface PaginationModel { rowsPerPage: `${number}` page: `${number}` } type FilteringModel = Record<string, string> type FilteringAndPaginationModel = Partial<PaginationModel> & FilteringModel const obj: FilteringAndPaginationModel = { rowsPerPage: "1", }

bold perch
unkempt hamlet
#

How would you suggest changing to match the replied suggestion?

bold perch
#

what you wrote here is one way:

nesting it in a { filtering: } doesn't really make sense, since I would need to "unnest" it later anyways
or what i wrote here:
maybe you don't even need to store these in one obj and can instead keep pagination and filtering as separate values until they need to be packed up into the request body?

but i don't really know what your application does so i'm not sure what makes the most sense. that's why i kept asking for context (like example code that uses values of these types)

#

maybe a type like {filtering: FilteringModel, pagination: PaginationModel} would be nicer ¯_(ツ)_/¯

unkempt hamlet
#

I suppose doing that {filtering: ... } may work out... I'd just need to do some changes to make it fit right.

#

But negated types would definitely work. It's always a bit of a shame when I see like 5 year old TypeScript proposals that would be super useful, but never implemented

bold perch
#

yeah i've definitely wished negated types were a thing as well. it's a tricky language feature though with a lot of implications. Wes made an attempt at implementation a few years ago and you might be interested in skimming the discussion: https://github.com/microsoft/TypeScript/pull/29317

unkempt hamlet
#

Partial type argument inference is another one that I would like. Without it you have to do some hacky workarounds using "currying" and whatnot

bold perch
#

yes very much so. i have higher hopes for that to be implemented before negated types

unkempt hamlet
#

It certainly seems easier then negated types. Negated types takes some deep logic thoughts to implement, lots of possible branches imo

#

Whereas partial type is basically just a note to ignore

bold perch
#

yeah