Skip to content

Commit d9708da

Browse files
committed
feat(type-manipulation): added cast, castjson and frombytes
1 parent 7a7c2de commit d9708da

File tree

3 files changed

+193
-1
lines changed

3 files changed

+193
-1
lines changed

README.md

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,9 @@ Type manipulation helpers:
290290
- [CoalesceSliceOrEmpty](#coalescesliceorempty)
291291
- [CoalesceMap](#coalescemap)
292292
- [CoalesceMapOrEmpty](#coalescemaporempty)
293+
- [Cast](#cast)
294+
= [CastJSON](#castjson)
295+
- [FromBytes](#frombytes)
293296

294297
Function helpers:
295298

@@ -3431,6 +3434,84 @@ result := lo.CoalesceMapOrEmpty(nil, map[string]int{})
34313434
// {}
34323435
```
34333436

3437+
### Cast
3438+
3439+
Converts any type to a given type. If conversion fails, it returns the zero value of the given type. This is a type-safe version of type assertion.
3440+
3441+
Cast enforces strict type assertion, for example trying to convert a number of 10 to float64 will return 0.0 instead of 10.0.
3442+
3443+
```go
3444+
var n any = 10
3445+
3446+
fmt.Println(lo.Cast[int](n) == 10) // true
3447+
3448+
fmt.Println(lo.Cast[float64](n) == 10.0) // false
3449+
```
3450+
3451+
[[play](https://go.dev/play/p/IxL5n8tOvTW)]
3452+
3453+
3454+
### CastJSON
3455+
3456+
Converts any type to a given type based on their json representations. It partially fills the target in case they are not directly compatible. Any errors are ignored.
3457+
3458+
```go
3459+
type TestObject1 struct {
3460+
Foo string `json:"foo"`
3461+
Bar int `json:"bar"`
3462+
}
3463+
3464+
type TestObject2 struct {
3465+
Foo string `json:"foo"`
3466+
Bar int `json:"bar"`
3467+
Qux string `json:"qux"`
3468+
}
3469+
3470+
testObject1 := TestObject1{
3471+
Foo: "bar",
3472+
Bar: 42,
3473+
}
3474+
3475+
testObject2 := TestObject2{
3476+
Foo: "bar",
3477+
Bar: 42,
3478+
Qux: "baz",
3479+
}
3480+
3481+
castedTestObject1 := lo.CastJSON[TestObject1](testObject2)
3482+
3483+
fmt.Println(castedTestObject1.Foo == testObject1.Foo) // true
3484+
fmt.Println(castedTestObject1.Bar == testObject1.Bar) // true
3485+
```
3486+
3487+
[[play](https://go.dev/play/p/s42Brn9UOug)]
3488+
3489+
### FromBytes
3490+
3491+
Converts a byte array to a given type. Ignores any errors.
3492+
3493+
```go
3494+
bytes, _ := json.Marshal("foo")
3495+
3496+
fmt.Println(lo.FromBytes[string](bytes) == "foo") // true
3497+
3498+
type TestObject struct {
3499+
Foo string `json:"foo"`
3500+
Bar int `json:"bar"`
3501+
}
3502+
3503+
testObject := TestObject{
3504+
Foo: "bar",
3505+
Bar: 42,
3506+
}
3507+
3508+
bytes, _ = json.Marshal(testObject)
3509+
3510+
fmt.Println(lo.FromBytes[TestObject](bytes) == testObject) // true
3511+
```
3512+
3513+
[[play](https://go.dev/play/p/T9dfh5QPEzq)]
3514+
34343515
### Partial
34353516

34363517
Returns new function that, when called, has its first argument set to the provided value.

type_manipulation.go

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package lo
22

3-
import "reflect"
3+
import (
4+
"encoding/json"
5+
"reflect"
6+
)
47

58
// IsNil checks if a value is nil or if it's a reference type with a nil underlying value.
69
func IsNil(x any) bool {
@@ -187,3 +190,27 @@ func CoalesceMapOrEmpty[K comparable, V any](v ...map[K]V) map[K]V {
187190
}
188191
return map[K]V{}
189192
}
193+
194+
// Converts any type to a given type. If conversion fails, it returns the zero value of the given type. This is a type-safe version of type assertion.
195+
//
196+
// Cast enforces strict type assertion, for example trying to convert a number of 10 to float64 will return 0.0 instead of 10.0.
197+
func Cast[T any](val any) T {
198+
if val, ok := val.(T); ok {
199+
return val
200+
}
201+
var zero T
202+
return zero
203+
}
204+
205+
// Converts any type to a given type based on their json representations. It partially fills the target in case they are not directly compatible. Any errors are ignored.
206+
func CastJSON[T any](val any) T {
207+
bytes, _ := json.Marshal(val)
208+
return FromBytes[T](bytes)
209+
}
210+
211+
// Converts a byte array to a given type. Ignores any errors.
212+
func FromBytes[T any](bytes []byte) T {
213+
var v T
214+
_ = json.Unmarshal(bytes, &v)
215+
return v
216+
}

type_manipulation_test.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package lo
22

33
import (
4+
"encoding/json"
45
"testing"
56

67
"github.com/stretchr/testify/assert"
@@ -566,3 +567,86 @@ func TestCoalesceMapOrEmpty(t *testing.T) {
566567
is.NotNil(result10)
567568
is.Equal(map1, result10)
568569
}
570+
571+
func TestCast(t *testing.T) {
572+
t.Parallel()
573+
is := assert.New(t)
574+
575+
var n any = 10
576+
577+
is.Equal(10, Cast[int](n))
578+
579+
is.True(Cast[int](n) > 0)
580+
581+
is.NotZero(Cast[int](n))
582+
583+
is.Zero(Cast[int](nil))
584+
585+
is.Zero(Cast[string](n))
586+
587+
is.Zero(Cast[int32](n))
588+
589+
is.Zero(Cast[int64](n))
590+
591+
is.Zero(Cast[uint](n))
592+
593+
is.Zero(Cast[float32](n))
594+
595+
is.Zero(Cast[float64](n))
596+
}
597+
598+
func TestCastJSON(t *testing.T) {
599+
t.Parallel()
600+
is := assert.New(t)
601+
602+
type TestObject1 struct {
603+
Foo string `json:"foo"`
604+
Bar int `json:"bar"`
605+
}
606+
607+
type TestObject2 struct {
608+
Foo string `json:"foo"`
609+
Bar int `json:"bar"`
610+
Qux string `json:"qux"`
611+
}
612+
613+
testObject1 := TestObject1{
614+
Foo: "bar",
615+
Bar: 42,
616+
}
617+
618+
testObject2 := TestObject2{
619+
Foo: "bar",
620+
Bar: 42,
621+
Qux: "baz",
622+
}
623+
624+
is.Equal(testObject1, CastJSON[TestObject1](testObject2))
625+
}
626+
627+
func TestFromBytes(t *testing.T) {
628+
t.Parallel()
629+
is := assert.New(t)
630+
631+
is.Zero(FromBytes[string]([]byte{}))
632+
633+
bytes, _ := json.Marshal("foo")
634+
635+
is.Equal("foo", FromBytes[string](bytes))
636+
637+
bytes, _ = json.Marshal(42)
638+
639+
is.Equal(42, FromBytes[int](bytes))
640+
641+
type TestObject struct {
642+
Foo string `json:"foo"`
643+
Bar int `json:"bar"`
644+
}
645+
646+
testObject := TestObject{
647+
Foo: "bar",
648+
Bar: 42,
649+
}
650+
651+
is.Equal(testObject, FromBytes[TestObject]([]byte(`{"foo":"bar","bar":42}`)))
652+
}

0 commit comments

Comments
 (0)