Overload not typing object literals
shouldn’t only “real” show up as a property here? if not, how could I achieve that?
53 Replies
apologies for the shitty screenshot I’m on my mom’s laptop rn lmfao
Based on the documentation, it seems like you're asking about TypeScript's type inference and how it interacts with object literals and function overloads. However, without a specific code example, it's difficult to provide a precise answer.
In general, TypeScript uses static types and type inference to determine the properties and methods available on an object. If you're seeing unexpected properties, it might be due to the way your types or interfaces are defined.
If you're working with function overloads, the order of the overload signatures is important. TypeScript will use the first matching overload definition.
If you could provide a specific code example, I could give a more accurate answer. 🦕
I did provide a code example
???
lines 7 through 18
bro it's AI. AI can't read your screenshot
oh lmao what
I thought someone came in and ran a command or something
here's the code example sir since you cannot read screenshots
I would have expected only the
real
property to show up in the object literal, since that's the overload I've chosen from true
. If I type fake
, it errors out.The behavior you're seeing is due to TypeScript's structural typing system. When you call
A(true, {...})
, TypeScript doesn't immediately know which overload to pick. It sees that both Real
and Fake
are objects and that they could potentially match the object literal you're providing. Therefore, it suggests both real
and fake
properties.
However, once you start typing the object literal, TypeScript can start narrowing down which overload is correct. If you type { real: ... }
, it will match the Real
overload, and if you type { fake: ... }
, it will match the Fake
overload. If you provide a property that doesn't match the overload you're trying to use (like using fake
when real
is true
), TypeScript will give an error.
If you want to avoid this, you could make your types more distinct so TypeScript can tell them apart more easily. For example, you could add a required type
field to your Real
and Fake
types:
Now, TypeScript can tell the difference between Real
and Fake
as soon as you start typing the object literal, and it will only suggest the correct properties.why can't it tell which object to use from the boolean? it explicitly states which object goes with what
The TypeScript compiler doesn't evaluate the actual values of the arguments when determining which overload to use. Instead, it looks at the types of the arguments. In your case, the
real
argument is of type boolean
, which could be either true
or false
. Therefore, TypeScript considers both overloads as potential matches.
When you're calling the function A(true, {...})
, TypeScript doesn't immediately know which overload to pick based on the true
value. It sees that both Real
and Fake
are objects and that they could potentially match the object literal you're providing. Therefore, it suggests both real
and fake
properties.
However, once you start typing the object literal, TypeScript can start narrowing down which overload is correct. If you type { real: ... }
, it will match the Real
overload, and if you type { fake: ... }
, it will match the Fake
overload. If you provide a property that doesn't match the overload you're trying to use (like using fake
when real
is true
), TypeScript will give an error.
This behavior is due to TypeScript's structural typing system. It's not about the actual values, but about the types of the values.by this logic, shouldn't this be able to tell which object to use based on whether the type is a boolean or string?
In your code example, TypeScript will indeed be able to determine which overload to use based on whether the
version
argument is a string 'V2'
or a boolean.
When you call A('V2', {...})
, TypeScript knows to use the V2
overload, and it will only suggest the v2
property when you start typing the object literal. If you provide a property that doesn't match the V2
type (like using v3
), TypeScript will give an error.
Similarly, if you call A(true, {...})
or A(false, {...})
, TypeScript knows to use the V3
overload, and it will only suggest the v3
property when you start typing the object literal. If you provide a property that doesn't match the V3
type (like using v2
), TypeScript will give an error.
This is because TypeScript uses the types of the arguments to determine which overload to use. In this case, the types are distinct ('V2'
is not a boolean), so TypeScript can tell the overloads apart based on the version
argument.except I can see both v2 and v3 as properties in intellisense
The behavior you're seeing is due to TypeScript's structural typing system. When you call
A('V2', {...})
, TypeScript doesn't immediately know which overload to pick. It sees that both V2
and V3
are objects and that they could potentially match the object literal you're providing. Therefore, it suggests both v2
and v3
properties.
However, once you start typing the object literal, TypeScript can start narrowing down which overload is correct. If you type { v2: ... }
, it will match the V2
overload, and if you type { v3: ... }
, it will match the V3
overload. If you provide a property that doesn't match the overload you're trying to use (like using v3
when version
is 'V2'
), TypeScript will give an error.
This behavior is due to TypeScript's structural typing system. It's not about the actual values, but about the types of the values.but you said
it will only suggest the v2 property
I apologize for the confusion. The TypeScript compiler uses the types of the arguments to determine which overload to use. However, when it comes to providing suggestions via Intellisense in your code editor, it might still show all possible properties from all overloads until you start typing and it can infer the correct type. This behavior can vary depending on the code editor and its TypeScript support.
In your case, even though you've specified 'V2' or boolean as the argument, the editor's Intellisense might still show both
v2
and v3
properties as it doesn't evaluate the actual values of the arguments. Once you start typing the object literal, TypeScript can start narrowing down which overload is correct.
Please note that this is a limitation of the editor's Intellisense and not TypeScript's type system. The TypeScript compiler itself will correctly match the overloads based on the provided arguments when the code is compiled.I am probably gonna start a robot revolution if I keep bugging this AI another question: is it possible to remove every
readonly
component on a type?Yes, it is possible to remove the
readonly
modifier from properties of a type in TypeScript. You can do this by using the -readonly
modifier in a mapped type. Here's an example:
In this example, MutableType
is a mapped type that goes through each property (P
) in ReadOnlyType
. For each property, it removes the readonly
modifier and keeps the original type of the property (ReadOnlyType[P]
). The resulting MutableType
has the same properties as ReadOnlyType
, but none of them are readonly
.
Please note that this only removes the readonly
modifier from the properties of the type itself. If a property is an object that has its own readonly
properties, those will not be made mutable. You would need to apply the mapped type to those properties as well if you want them to be mutable.can this be done recursively?
The documentation does not provide specific information on whether the removal of the
readonly
modifier from properties of a type can be done recursively. The provided method only removes the readonly
modifier from the properties of the type itself. If a property is an object that has its own readonly
properties, those will not be made mutable by this method. You would need to apply the mapped type to those properties as well if you want them to be mutable. However, a recursive solution would require a more complex type manipulation which is not directly covered in the provided documentation.would you be able to make one?
I'm sorry, but I need more context to provide a helpful response. Could you please clarify what you would like me to make?
a type that recursively removes readonly from every property
Sure, here's a TypeScript type that recursively removes
readonly
from every property:
This type uses a combination of mapped types and conditional types to recursively walk through every property of T
. If a property is an array, it makes the elements of the array mutable. If a property is an object (but not a function), it makes the properties of that object mutable. If a property is a primitive value or a function, it leaves it as is.
Here's how you can use it:
In this example, MutableType
has the same structure as ReadOnlyType
, but none of the properties are readonly
, even in nested objects.DeepMutable
produces the following warning:
The warning you're seeing is from Deno's linter, which discourages the use of
Function
as a type. When you use Function
as a type, you're saying that a value can be any function or class, without specifying what arguments the function should take or what it should return. This can lead to issues with type safety, because TypeScript can't check that you're using the function correctly.
In the DeepMutable
type, this line is likely causing the warning:
This line checks if a property is a function. If it is, the property is left as is. The linter warning is saying that this isn't safe, because it doesn't provide any information about what the function's arguments or return type should be.
To fix this warning, you would need to replace Function
with a more specific function type. However, in this case, it might not be straightforward to do so. The DeepMutable
type is meant to be a general utility type that can be used with any object structure, and the functions within that object could have any number of arguments and any return type.
If you're sure that your usage of Function
here is safe and you want to ignore the linter warning, you can do so by adding a // deno-lint-ignore ban-types
comment on the line before the warning:
This will tell Deno's linter to ignore the ban-types
rule on the next line.could you make the same thing that ADDS readonly to everything?
Sure, here's a TypeScript type that recursively adds
readonly
to every property:
This type uses a combination of mapped types and conditional types to recursively walk through every property of T
. If a property is an array, it makes the elements of the array readonly. If a property is an object, it makes the properties of that object readonly. If a property is a primitive value, it leaves it as is.
Here's how you can use it:
In this example, ReadonlyType
has the same structure as MutableType
, but all of the properties are readonly
, even in nested objects.this causes functions to have the
DeepReadonly
property on them which causes them to not be callableThe
DeepReadonly
type provided earlier is designed to make all properties of an object readonly
, including functions. However, making a function readonly
doesn't prevent it from being callable. It only prevents the function from being reassigned.
Here's an example:
In this example, example.prop2
is a function, and it's still callable even though it's readonly
. The readonly
modifier only prevents example.prop2
from being reassigned to a different function.
If you're seeing an error that a function is not callable, it might be due to a different issue. If you could provide more details about the error and the code that's causing it, I might be able to help you troubleshoot it.
this produces an error:
The error message is indicating that the
push
method does not exist on the type DeepReadonly<typeof rm.geometry>
. This is because the DeepReadonly
type recursively makes all properties of an object readonly
, and in TypeScript, readonly
arrays do not have the push
method.
In your code, rm.geometry()
likely returns an array (since you're able to call push
on it), and DeepReadonly<typeof rm.geometry>
represents a function that returns a readonly version of that array. Since readonly arrays in TypeScript don't have the push
method, you're seeing an error when you try to call push
on b
.
If you want to make a deep readonly copy of the array returned by rm.geometry()
, you should call the function and then apply DeepReadonly
to the result, like this:
In this code, b
is a deep readonly copy of the array a
, and trying to call push
on b
will result in a TypeScript error.rm.geometry()
is not an array, sorry. hopefully this is more clearThe
DeepReadonly
type provided earlier is designed to make all properties of an object readonly
, including functions. However, making a function readonly
doesn't prevent it from being callable. It only prevents the function from being reassigned.
Here's an example:
In this example, example.prop2
is a function, and it's still callable even though it's readonly
. The readonly
modifier only prevents example.prop2
from being reassigned to a different function.
If you're seeing an error that a function is not callable, it might be due to a different issue. If you could provide more details about the error and the code that's causing it, I might be able to help you troubleshoot it.
this produces the following error:
The error you're seeing is due to how the
DeepReadonly
type is defined. It recursively applies readonly
to all properties of an object, including functions. In TypeScript, when you apply readonly
to a function, it means that the function itself cannot be reassigned, but it doesn't prevent the function from being called.
However, in your DeepReadonly
type, when it encounters a function (which is an object in JavaScript), it recursively applies DeepReadonly
to the function's properties, not the function itself. This is why you're seeing the error that the function is not callable.
One way to fix this is to modify the DeepReadonly
type to handle functions differently. Here's an example:
In this version of DeepReadonly
, when it encounters a function (identified by (...args: any[]) => any
), it leaves the function as-is instead of trying to apply DeepReadonly
to the function's properties. This should allow you to call methods on DeepReadonly
objects.I get a warning from
any
The warning you're seeing is likely due to TypeScript's
noImplicitAny
option, which is enabled by default in Deno. This option triggers a warning whenever the any
type is used implicitly, such as when a function parameter's type is not specified.
To resolve this warning, you should explicitly specify types wherever possible. If you intentionally want to use the any
type, you can do so explicitly to avoid the warning. For example:
In this code, bar
is explicitly typed as any
, so you won't see a warning.
However, using any
is generally discouraged in TypeScript because it bypasses type checking. If you're not sure what type a variable should be, it's often better to use unknown
instead. This forces you to perform some kind of type checking before you can use the variable.
If you want to disable the noImplicitAny
option, you can do so by creating a tsconfig.json
file and setting noImplicitAny
to false
. However, this is generally not recommended because it makes your code less type-safe.ok more problems, I get a massive error here:
from the expression going into
geometry.push
in this function:
getActiveDiff().geometry
is Geometry[]
and this outer push
function is in the Geometry
class.
here's what the copy function looks like:
The error message is indicating that the TypeScript compiler is having trouble assigning the result of the
copy(this)
function to the push
method of the Geometry
class. This is because the copy
function returns a DeepMutable<T>
, but the push
method is expecting a Geometry
.
The DeepMutable<T>
type is a utility type that makes all properties of T
mutable (i.e., not readonly
). However, it seems like the push
method of the Geometry
class is not designed to handle this kind of object.
One way to fix this issue is to ensure that the copy
function returns a Geometry
object when it's called with a Geometry
argument. This could be done by adding a type guard to the copy
function:
This tells TypeScript that if obj
is a Geometry
, the function will return a Geometry
. Otherwise, it will return a DeepMutable<T>
.
However, this might not be the best solution if the copy
function is used in many places in your code, as it could potentially break other parts of your code that rely on the copy
function returning a DeepMutable<T>
.
Another solution could be to modify the push
method of the Geometry
class to accept a DeepMutable<Geometry>
:
This tells TypeScript that the copy(this)
expression will always return a DeepMutable<Geometry>
, which should be compatible with the push
method.
Please note that these solutions might not work perfectly depending on the rest of your code, and you might need to adjust them to fit your specific use case. If you're still having trouble, could you provide more details about theGeometry
class and the getActiveDiff
function?the problem is that
DeepMutable<Geometry>
is not compatible with the Geometry
arrayThe issue you're encountering is due to the type incompatibility between
DeepMutable<Geometry>
and Geometry
. The DeepMutable<T>
type is a utility type that makes all properties of T
mutable (i.e., not readonly
). However, the Geometry
array is expecting elements of type Geometry
, not DeepMutable<Geometry>
.
One way to resolve this issue is to ensure that the copy
function returns a Geometry
object when it's called with a Geometry
argument. This could be done by adding a type guard to the copy
function:
This tells TypeScript that if obj
is a Geometry
, the function will return a Geometry
. Otherwise, it will return a DeepMutable<T>
.
However, this might not be the best solution if the copy
function is used in many places in your code, as it could potentially break other parts of your code that rely on the copy
function returning a DeepMutable<T>
.
Another solution could be to modify the push
method of the Geometry
class to accept a DeepMutable<Geometry>
:
This tells TypeScript that the copy(this)
expression will always return a DeepMutable<Geometry>
, which should be compatible with the push
method.
Please note that these solutions might not work perfectly depending on the rest of your code, and you might need to adjust them to fit your specific use case. If you're still having trouble, could you provide more details about the Geometry
class and the getActiveDiff
function?ok so, I want a function that takes in an array of numbers and converts them from degrees to radians. The only thing is that:
- I want the function to prefer a tuple, so if I input
[0,0,0]
, I should get out [number, number, number]
- I want the function to allow for readonly inputs, since the returned array will be a new array from map
Sure, you can create a function in TypeScript that takes in an array of numbers (preferably a tuple) and returns a tuple of the same length. The function can also accept readonly inputs. Here's how you might do it:
In this function, the
readonly
keyword is used to indicate that the input array should not be modified. The function returns a new array (also marked as readonly
) that contains the radian values.
However, this function does not enforce that the input and output are tuples. In TypeScript, a tuple is a special kind of array with a fixed length and known types. If you want to enforce that the input and output are tuples of the same length, you would need to use a more complex type:
In this function, T
is a generic type that extends readonly number[]
, representing a tuple of numbers. The return type { [K in keyof T]: number }
is a mapped type that represents a tuple of the same length as T
, where each element is a number.
Please note that the as any
cast is necessary because TypeScript cannot guarantee that the map
function returns an array of the same length as the input. However, since we know that map
does not change the length of the array, this cast is safe.ok how about this, can you make the function also take in a
number
and return a converted number
?Yes, you can modify the function to accept either a single number or an array of numbers. This can be achieved using function overloads in TypeScript. Here's how you might do it:
In this code, the first two lines are function signatures that tell TypeScript what types of arguments the function can accept and what it should return. The third line is the actual function implementation.
If you call
degreesToRadians
with a single number, it will return a single number. If you call it with an array of numbers, it will return an array of numbers. The readonly
keyword is used to indicate that the input array should not be modified.this does not prefer a tuple,
degreesToRadians([1])
gives me number[]
when I want it to give me [number]
In TypeScript, the distinction between
number[]
and [number]
is subtle. Both represent an array of numbers, but [number]
is a tuple type that specifically represents an array with one element, while number[]
represents an array with any number of elements.
The function degreesToRadians
as currently written will always return number[]
, because the map
function always returns an array, and TypeScript does not have a way to infer that the returned array should have the same length as the input array.
If you want to enforce that the function returns a tuple when given a tuple, you could use a more complex type:
In this function, T
is a generic type that extends readonly number[]
, representing a tuple of numbers. The return type { [K in keyof T]: number }
is a mapped type that represents a tuple of the same length as T
, where each element is a number.
However, please note that this function will still return an array when called with an array. The tuple type is only enforced at compile time, and at runtime, there is no difference between an array and a tuple in JavaScript.the thing is that I want it to assume a raw input such as
degreesToRadians([0])
is a tuple, instead of assuming it's a number[]
In TypeScript, the distinction between
number[]
and [number]
is subtle. Both represent an array of numbers, but [number]
is a tuple type that specifically represents an array with one element, while number[]
represents an array with any number of elements.
The function degreesToRadians
as currently written will always return number[]
, because the map
function always returns an array, and TypeScript does not have a way to infer that the returned array should have the same length as the input array.
If you want to enforce that the function returns a tuple when given a tuple, you could use a more complex type:
In this function, T
is a generic type that extends readonly number[]
, representing a tuple of numbers. The return type { [K in keyof T]: number }
is a mapped type that represents a tuple of the same length as T
, where each element is a number.
However, please note that this function will still return an array when called with an array. The tuple type is only enforced at compile time, and at runtime, there is no difference between an array and a tuple in JavaScript.I'll give you an example of what I mean.
the only with this is, as soon as I try to include readonly functionality everything breaks unexpectedly:
The issue you're encountering is due to the way TypeScript handles
Readonly
types. When you use Readonly<T>
, TypeScript treats the input as an immutable version of T
. However, when T
is a union type like [] | number[]
, TypeScript can't guarantee that the input is a tuple, so it defaults to treating it as an array.
In your case, when you call preferTuple([1,2])
, TypeScript sees the input as an array of numbers, not a tuple, because it's being passed as a Readonly
type. This is why you're seeing (1, 2)[]
as the output instead of [number, number]
.
When you call preferTuple([1,2] as [number, number])
, TypeScript knows that the input is a tuple because you've explicitly cast it as such. This is why you're seeing [number, number]
as the output.
Finally, when you call preferTuple([1,2] as Readonly<[number, number]>)
, TypeScript treats the input as a readonly array, not a tuple, because of the Readonly
type. This is why you're seeing number | []
as the output.
If you want TypeScript to treat the input as a tuple, you'll need to explicitly cast it as such. Unfortunately, TypeScript doesn't currently have a way to infer that a readonly array should be treated as a tuple.would there be any way to detect if the input is
Readonly
and make a mutable type out of it to infer it that way?The documentation does not provide a direct way to detect if a type is
Readonly
and then make a mutable type out of it. TypeScript's type system does not provide a built-in mechanism for this.
However, you can create a type that removes the readonly
modifier from all properties of a type using mapped types. Here's an example:
This Mutable
type takes a type T
and produces a new type where all properties are mutable. The -readonly
syntax in the mapped type removes the readonly
modifier from each property.
You can use this Mutable
type to create a mutable version of a Readonly
type. For example:
In this example, ReadonlyPoint
is a Readonly
version of { x: number; y: number }
, and MutablePoint
is a mutable version of ReadonlyPoint
.
However, this does not infer whether the input type was Readonly
or not. It simply ensures that the resulting type is mutable. If the input type was not Readonly
, Mutable<T>
will be identical to T
.