#.
25 messages · Page 1 of 1 (latest)
there are a few different ways to approach this kind of function and the best option will depend on specifics. i imagine your real fooHandler is more involved than this. what does it do? perhaps most importantly: are you constructing new values to return or just returning the argument directly like in your example?
i figured it'd be something like that. i think you'll need to use overload signatures (or a conditional return type, but those are annoying and require assertions)
for example:
Preview:ts ... function fooHandler(obj: FooBarObj): FooBarObj function fooHandler(obj: FooObj): FooObj function fooHandler(obj: FooObj | FooBarObj) { if ("bar" in obj) { return obj // ^? } return obj // ^? } ...
i'm not sure if you're able/willing to change the inputs, but if this was a discriminated union then it'd be easier to work with
another option (which i'm guessing you've already considered and found unsatisfying) is to use a type like this:
type Obj = {
foo: number[]
bar?: string[]
}
for completeness's sake, if the function was simpler you could have used a generic identity function (but i don't think this will work for your use case):
Preview:ts ... function fooHandler<T extends FooObj | FooBarObj>( obj: T ): T { ...
the <...> syntax is what makes the function/type "generic". the stuff that goes in ... are referred to as "type parameters"
an "identity function" is any function that returns its argument unchanged
simplest being like x => x
this handbook page might help:
!hb generics
i'm not sure exactly what you mean by "settled", but i suspect it's related to why i said "i don't think this will work for your use case" in reference to the generic approach earlier
if you can share a more complete/realistic example i might be able to offer more specific suggestions, but i'm assuming you'll need to use the overload approach i shared earlier instead of this one
the question mark means it's optional. those two types are almost equivalent (with the exception that { foo: fooThings, bar: undefined } is assignable to the latter but not the former unless you've enabled exactOptionalPropertyTypes)
actually i lied, { foo: fooThings, bar: undefined } is assignable to both as well
Preview:```ts
type FooThings = "foo"
type BarThings = "bar"
type FooBar1 =
| {foo: FooThings}
| {foo: FooThings; bar: BarThings}
type FooBar2 = {foo: FooThings; bar?: BarThings}
const a1: FooBar1 = {foo: "foo"}
const a2: FooBar2 = {foo: "foo"}
const b1: FooBar1 = {foo: "foo", bar: "bar"}
...```
if OnlyHas is meant to prevent other properties from existing at runtime, that's not possible (the terms "excess properties" and "exact types" are usually used for this in TS in case you want to read more)
in TS all types can be subtyped (by adding or refining properties), and a value of a given type is always assignable to supertypes of that type
i'm still not actually sure why you need that. examples would help
rough
feel free to send it anyway, i may not be able to digest it right away though