Repository: diegoholiveira/jsonlogic
Pull request #95 (2025-01-05 15:54:16)

Go 1.18 as minimum requirement

codecov-commenter: ## [Codecov](https://app.codecov.io/gh/diegoholiveira/jsonlogic/pull/95?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Diego+Henrique+Oliveira) Report
Attention: Patch coverage is `98.54015%` with `2 lines` in your changes missing coverage. Please review.
> Project coverage is 93.26%. Comparing base [(`c321e8c`)](https://app.codecov.io/gh/diegoholiveira/jsonlogic/commit/c321e8cc5ba06731b18454151ffcc0e235883438?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Diego+Henrique+Oliveira) to head [(`7396d4e`)](https://app.codecov.io/gh/diegoholiveira/jsonlogic/commit/7396d4e3516bb895fa9044a83f26d24cd00d7de3?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Diego+Henrique+Oliveira).
> Report is 1 commits behind head on main.

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##             main      #95   +/-   ##
=======================================
  Coverage   93.26%   93.26%           
=======================================
  Files           9        9           
  Lines        1025     1025           
=======================================
  Hits          956      956           
  Misses         49       49           
  Partials       20       20           
```

</details>

Patch:
diff --git a/arrays.go b/arrays.go
index 7523a29..30c6feb 100644
--- a/arrays.go
+++ b/arrays.go
@@ -12,10 +12,10 @@ func (e ErrReduceDataType) Error() string {
 	return fmt.Sprintf("The type \"%s\" is not supported", e.dataType)
 }
 
-func filter(values, data interface{}) interface{} {
-	parsed := values.([]interface{})
+func filter(values, data any) any {
+	parsed := values.([]any)
 
-	var subject interface{}
+	var subject any
 
 	if isSlice(parsed[0]) {
 		subject = parsed[0]
@@ -25,7 +25,7 @@ func filter(values, data interface{}) interface{} {
 		subject = apply(parsed[0], data)
 	}
 
-	result := make([]interface{}, 0)
+	result := make([]any, 0)
 
 	if subject == nil {
 		return result
@@ -33,7 +33,7 @@ func filter(values, data interface{}) interface{} {
 
 	logic := solveVars(parsed[1], data)
 
-	for _, value := range subject.([]interface{}) {
+	for _, value := range subject.([]any) {
 		v := parseValues(logic, value)
 
 		if isTrue(v) {
@@ -44,10 +44,10 @@ func filter(values, data interface{}) interface{} {
 	return result
 }
 
-func _map(values, data interface{}) interface{} {
-	parsed := values.([]interface{})
+func _map(values, data any) any {
+	parsed := values.([]any)
 
-	var subject interface{}
+	var subject any
 
 	if isSlice(parsed[0]) {
 		subject = parsed[0]
@@ -57,7 +57,7 @@ func _map(values, data interface{}) interface{} {
 		subject = apply(parsed[0], data)
 	}
 
-	result := make([]interface{}, 0)
+	result := make([]any, 0)
 
 	if subject == nil {
 		return result
@@ -65,7 +65,7 @@ func _map(values, data interface{}) interface{} {
 
 	logic := solveVars(parsed[1], data)
 
-	for _, value := range subject.([]interface{}) {
+	for _, value := range subject.([]any) {
 		v := parseValues(logic, value)
 
 		if isTrue(v) || isNumber(v) || isBool(v) {
@@ -76,10 +76,10 @@ func _map(values, data interface{}) interface{} {
 	return result
 }
 
-func reduce(values, data interface{}) interface{} {
-	parsed := values.([]interface{})
+func reduce(values, data any) any {
+	parsed := values.([]any)
 
-	var subject interface{}
+	var subject any
 
 	if isSlice(parsed[0]) {
 		subject = parsed[0]
@@ -94,7 +94,7 @@ func reduce(values, data interface{}) interface{} {
 	}
 
 	var (
-		accumulator interface{}
+		accumulator any
 		valueType   string
 	)
 
@@ -120,13 +120,13 @@ func reduce(values, data interface{}) interface{} {
 		}
 	}
 
-	context := map[string]interface{}{
+	context := map[string]any{
 		"current":     float64(0),
 		"accumulator": accumulator,
 		"valueType":   valueType,
 	}
 
-	for _, value := range subject.([]interface{}) {
+	for _, value := range subject.([]any) {
 		if value == nil {
 			continue
 		}
diff --git a/comp.go b/comp.go
index 84c3d7b..1b67ef6 100644
--- a/comp.go
+++ b/comp.go
@@ -7,7 +7,7 @@ import (
 )
 
 // at simulate undefined in javascript
-func at(values []interface{}, index int) interface{} {
+func at(values []any, index int) any {
 	if index >= 0 && index < len(values) {
 		return values[index]
 	}
@@ -16,7 +16,7 @@ func at(values []interface{}, index int) interface{} {
 
 type undefinedType struct{}
 
-func toNumberForLess(v interface{}) float64 {
+func toNumberForLess(v any) float64 {
 	switch value := v.(type) {
 	case nil:
 		return 0
@@ -45,7 +45,7 @@ func toNumberForLess(v interface{}) float64 {
 
 // less reference javascript implementation
 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Less_than#description
-func less(a, b interface{}) bool {
+func less(a, b any) bool {
 	// If both values are strings, they are compared as strings,
 	// based on the values of the Unicode code points they contain.
 	if isString(a) && isString(b) {
@@ -56,7 +56,7 @@ func less(a, b interface{}) bool {
 	return toNumberForLess(b) > toNumberForLess(a)
 }
 
-func hardEquals(a, b interface{}) bool {
+func hardEquals(a, b any) bool {
 	ra := reflect.ValueOf(a).Kind()
 	rb := reflect.ValueOf(b).Kind()
 
@@ -67,7 +67,7 @@ func hardEquals(a, b interface{}) bool {
 	return equals(a, b)
 }
 
-func equals(a, b interface{}) bool {
+func equals(a, b any) bool {
 	// comparison to a nil value is falsy
 	if a == nil || b == nil {
 		// if a and b is nil, return true, else return falsy
diff --git a/go.mod b/go.mod
index ba0f999..bb90854 100644
--- a/go.mod
+++ b/go.mod
@@ -1,8 +1,14 @@
 module github.com/diegoholiveira/jsonlogic/v3
 
-go 1.14
+go 1.18
 
 require (
 	github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df
-	github.com/stretchr/testify v1.6.1
+	github.com/stretchr/testify v1.10.0
+)
+
+require (
+	github.com/davecgh/go-spew v1.1.1 // indirect
+	github.com/pmezard/go-difflib v1.0.0 // indirect
+	gopkg.in/yaml.v3 v3.0.1 // indirect
 )
diff --git a/go.sum b/go.sum
index 7deba71..a63e1f3 100644
--- a/go.sum
+++ b/go.sum
@@ -1,13 +1,12 @@
 github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df h1:GSoSVRLoBaFpOOds6QyY1L8AX7uoY+Ln3BHc22W40X0=
 github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df/go.mod h1:hiVxq5OP2bUGBRNS3Z/bt/reCLFNbdcST6gISi1fiOM=
-github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
-github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
-github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
-gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/helpers.go b/helpers.go
index d50fe0b..166bab0 100644
--- a/helpers.go
+++ b/helpers.go
@@ -5,19 +5,19 @@ import (
 	"strconv"
 )
 
-func is(obj interface{}, kind reflect.Kind) bool {
+func is(obj any, kind reflect.Kind) bool {
 	return obj != nil && reflect.TypeOf(obj).Kind() == kind
 }
 
-func isBool(obj interface{}) bool {
+func isBool(obj any) bool {
 	return is(obj, reflect.Bool)
 }
 
-func isString(obj interface{}) bool {
+func isString(obj any) bool {
 	return is(obj, reflect.String)
 }
 
-func isNumber(obj interface{}) bool {
+func isNumber(obj any) bool {
 	switch obj.(type) {
 	case int, float64:
 		return true
@@ -26,19 +26,19 @@ func isNumber(obj interface{}) bool {
 	}
 }
 
-func isPrimitive(obj interface{}) bool {
+func isPrimitive(obj any) bool {
 	return isBool(obj) || isString(obj) || isNumber(obj)
 }
 
-func isMap(obj interface{}) bool {
+func isMap(obj any) bool {
 	return is(obj, reflect.Map)
 }
 
-func isSlice(obj interface{}) bool {
+func isSlice(obj any) bool {
 	return is(obj, reflect.Slice)
 }
 
-func isTrue(obj interface{}) bool {
+func isTrue(obj any) bool {
 	if isBool(obj) {
 		return obj.(bool)
 	}
@@ -56,8 +56,8 @@ func isTrue(obj interface{}) bool {
 	return false
 }
 
-func toSliceOfNumbers(values interface{}) []float64 {
-	_values := values.([]interface{})
+func toSliceOfNumbers(values any) []float64 {
+	_values := values.([]any)
 
 	numbers := make([]float64, len(_values))
 	for i, n := range _values {
@@ -66,26 +66,26 @@ func toSliceOfNumbers(values interface{}) []float64 {
 	return numbers
 }
 
-func toNumber(value interface{}) float64 {
+func toNumber(value any) float64 {
 	if isString(value) {
 		w, _ := strconv.ParseFloat(value.(string), 64)
 
 		return w
 	}
 
-	switch value.(type) {
+	switch value := value.(type) {
 	case int:
-		return float64(value.(int))
+		return float64(value)
 	default:
 		return value.(float64)
 	}
 }
 
-func toString(value interface{}) string {
+func toString(value any) string {
 	if isNumber(value) {
-		switch value.(type) {
+		switch value := value.(type) {
 		case int:
-			return strconv.FormatInt(int64(value.(int)), 10)
+			return strconv.FormatInt(int64(value), 10)
 		default:
 			return strconv.FormatFloat(value.(float64), 'f', -1, 64)
 		}
diff --git a/jsonlogic.go b/jsonlogic.go
index b298393..8d347d7 100644
--- a/jsonlogic.go
+++ b/jsonlogic.go
@@ -17,7 +17,7 @@ func (e ErrInvalidOperator) Error() string {
 	return fmt.Sprintf("The operator \"%s\" is not supported", e.operator)
 }
 
-func between(operator string, values []interface{}, data interface{}) interface{} {
+func between(operator string, values []any, data any) any {
 	a := parseValues(values[0], data)
 	b := parseValues(values[1], data)
 	c := parseValues(values[2], data)
@@ -37,7 +37,7 @@ func between(operator string, values []interface{}, data interface{}) interface{
 	return less(c, b) && less(b, a)
 }
 
-func unary(operator string, value interface{}) interface{} {
+func unary(operator string, value any) any {
 	if operator == "+" || operator == "*" || operator == "/" {
 		return toNumber(value)
 	}
@@ -63,13 +63,13 @@ func unary(operator string, value interface{}) interface{} {
 	return b
 }
 
-func _and(values, data interface{}) interface{} {
+func _and(values, data any) any {
 	values = getValuesWithoutParsing(values, data)
 	var v float64
 
 	isBoolExpression := true
 
-	for _, value := range values.([]interface{}) {
+	for _, value := range values.([]any) {
 		value = parseValues(value, data)
 		if isSlice(value) {
 			return value
@@ -103,10 +103,10 @@ func _and(values, data interface{}) interface{} {
 	return v
 }
 
-func _or(values, data interface{}) interface{} {
+func _or(values, data any) any {
 	values = getValuesWithoutParsing(values, data)
 
-	for _, value := range values.([]interface{}) {
+	for _, value := range values.([]any) {
 		if isTrue(parseValues(value, data)) {
 			return value
 		}
@@ -115,8 +115,8 @@ func _or(values, data interface{}) interface{} {
 	return false
 }
 
-func _inRange(value interface{}, values interface{}) bool {
-	v := values.([]interface{})
+func _inRange(value any, values any) bool {
+	v := values.([]any)
 
 	i := v[0]
 	j := v[1]
@@ -128,7 +128,7 @@ func _inRange(value interface{}, values interface{}) bool {
 	return toString(value) >= toString(i) && toString(j) >= toString(value)
 }
 
-func _in(value interface{}, values interface{}) bool {
+func _in(value any, values any) bool {
 	if value == nil || values == nil {
 		return false
 	}
@@ -141,7 +141,7 @@ func _in(value interface{}, values interface{}) bool {
 		return false
 	}
 
-	for _, element := range values.([]interface{}) {
+	for _, element := range values.([]any) {
 		if isSlice(element) {
 			if _inRange(value, element) {
 				return true
@@ -166,8 +166,8 @@ func _in(value interface{}, values interface{}) bool {
 	return false
 }
 
-func max(values interface{}) interface{} {
-	converted := values.([]interface{})
+func max(values any) any {
+	converted := values.([]any)
 	size := len(converted)
 	if size == 0 {
 		return nil
@@ -185,8 +185,8 @@ func max(values interface{}) interface{} {
 	return bigger
 }
 
-func min(values interface{}) interface{} {
-	converted := values.([]interface{})
+func min(values any) any {
+	converted := values.([]any)
 	size := len(converted)
 	if size == 0 {
 		return nil
@@ -204,16 +204,16 @@ func min(values interface{}) interface{} {
 	return smallest
 }
 
-func merge(values interface{}, level int8) interface{} {
-	result := make([]interface{}, 0)
+func merge(values any, level int8) any {
+	result := make([]any, 0)
 
 	if isPrimitive(values) || level > 1 {
 		return append(result, values)
 	}
 
 	if isSlice(values) {
-		for _, value := range values.([]interface{}) {
-			_values := merge(value, level+1).([]interface{})
+		for _, value := range values.([]any) {
+			_values := merge(value, level+1).([]any)
 
 			result = append(result, _values...)
 		}
@@ -222,12 +222,12 @@ func merge(values interface{}, level int8) interface{} {
 	return result
 }
 
-func conditional(values, data interface{}) interface{} {
+func conditional(values, data any) any {
 	if isPrimitive(values) {
 		return values
 	}
 
-	parsed := values.([]interface{})
+	parsed := values.([]any)
 
 	length := len(parsed)
 
@@ -253,8 +253,8 @@ func conditional(values, data interface{}) interface{} {
 	return nil
 }
 
-func setProperty(value, data interface{}) interface{} {
-	_value := value.([]interface{})
+func setProperty(value, data any) any {
+	_value := value.([]any)
 
 	object := _value[0]
 
@@ -268,20 +268,20 @@ func setProperty(value, data interface{}) interface{} {
 		panic(err)
 	}
 
-	_modified := modified.(map[string]interface{})
+	_modified := modified.(map[string]any)
 	_modified[property] = parseValues(_value[2], data)
 
-	return interface{}(_modified)
+	return any(_modified)
 }
 
-func missing(values, data interface{}) interface{} {
+func missing(values, data any) any {
 	if isString(values) {
-		values = []interface{}{values}
+		values = []any{values}
 	}
 
-	missing := make([]interface{}, 0)
+	missing := make([]any, 0)
 
-	for _, _var := range values.([]interface{}) {
+	for _, _var := range values.([]any) {
 		_value := getVar(_var, data)
 
 		if _value == nil {
@@ -292,15 +292,15 @@ func missing(values, data interface{}) interface{} {
 	return missing
 }
 
-func missingSome(values, data interface{}) interface{} {
-	parsed := values.([]interface{})
+func missingSome(values, data any) any {
+	parsed := values.([]any)
 	number := int(toNumber(parsed[0]))
 	vars := parsed[1]
 
-	missing := make([]interface{}, 0)
-	found := make([]interface{}, 0)
+	missing := make([]any, 0)
+	found := make([]any, 0)
 
-	for _, _var := range vars.([]interface{}) {
+	for _, _var := range vars.([]any) {
 		_value := getVar(_var, data)
 
 		if _value == nil {
@@ -314,13 +314,13 @@ func missingSome(values, data interface{}) interface{} {
 		return missing
 	}
 
-	return make([]interface{}, 0)
+	return make([]any, 0)
 }
 
-func all(values, data interface{}) interface{} {
-	parsed := values.([]interface{})
+func all(values, data any) any {
+	parsed := values.([]any)
 
-	var subject interface{}
+	var subject any
 
 	if isMap(parsed[0]) {
 		subject = apply(parsed[0], data)
@@ -334,7 +334,7 @@ func all(values, data interface{}) interface{} {
 		return false
 	}
 
-	for _, value := range subject.([]interface{}) {
+	for _, value := range subject.([]any) {
 		conditions := solveVars(parsed[1], value)
 		v := apply(conditions, value)
 
@@ -346,10 +346,10 @@ func all(values, data interface{}) interface{} {
 	return true
 }
 
-func none(values, data interface{}) interface{} {
-	parsed := values.([]interface{})
+func none(values, data any) any {
+	parsed := values.([]any)
 
-	var subject interface{}
+	var subject any
 
 	if isMap(parsed[0]) {
 		subject = apply(parsed[0], data)
@@ -365,7 +365,7 @@ func none(values, data interface{}) interface{} {
 
 	conditions := solveVars(parsed[1], data)
 
-	for _, value := range subject.([]interface{}) {
+	for _, value := range subject.([]any) {
 		v := apply(conditions, value)
 
 		if isTrue(v) {
@@ -376,10 +376,10 @@ func none(values, data interface{}) interface{} {
 	return true
 }
 
-func some(values, data interface{}) interface{} {
-	parsed := values.([]interface{})
+func some(values, data any) any {
+	parsed := values.([]any)
 
-	var subject interface{}
+	var subject any
 
 	if isMap(parsed[0]) {
 		subject = apply(parsed[0], data)
@@ -393,7 +393,7 @@ func some(values, data interface{}) interface{} {
 		return false
 	}
 
-	for _, value := range subject.([]interface{}) {
+	for _, value := range subject.([]any) {
 		v := apply(
 			solveVars(
 				solveVars(parsed[1], data),
@@ -410,7 +410,7 @@ func some(values, data interface{}) interface{} {
 	return false
 }
 
-func parseValues(values, data interface{}) interface{} {
+func parseValues(values, data any) any {
 	if values == nil || isPrimitive(values) {
 		return values
 	}
@@ -419,9 +419,9 @@ func parseValues(values, data interface{}) interface{} {
 		return apply(values, data)
 	}
 
-	parsed := make([]interface{}, 0)
+	parsed := make([]any, 0)
 
-	for _, value := range values.([]interface{}) {
+	for _, value := range values.([]any) {
 		if isMap(value) {
 			parsed = append(parsed, apply(value, data))
 		} else {
@@ -436,7 +436,7 @@ func parseValues(values, data interface{}) interface{} {
 // values without parsing. This means that each of the returned values might be a subtree
 // of JSONLogic.
 // Used in lazy evaluation of "AND" and "OR" operators
-func getValuesWithoutParsing(values, data interface{}) interface{} {
+func getValuesWithoutParsing(values, data any) any {
 	if values == nil || isPrimitive(values) {
 		return values
 	}
@@ -445,11 +445,11 @@ func getValuesWithoutParsing(values, data interface{}) interface{} {
 		return apply(values, data)
 	}
 
-	return values.([]interface{})
+	return values.([]any)
 }
 
-func apply(rules, data interface{}) interface{} {
-	ruleMap := rules.(map[string]interface{})
+func apply(rules, data any) any {
+	ruleMap := rules.(map[string]any)
 
 	// A map with more than 1 key counts as a primitive
 	// end recursion
@@ -486,7 +486,7 @@ func apply(rules, data interface{}) interface{} {
 	}
 
 	// an empty-map rule should return an empty-map
-	return make(map[string]interface{})
+	return make(map[string]any)
 }
 
 // Apply read the rule and it's data from io.Reader, executes it
@@ -496,8 +496,8 @@ func Apply(rule, data io.Reader, result io.Writer) error {
 		data = strings.NewReader("{}")
 	}
 
-	var _rule interface{}
-	var _data interface{}
+	var _rule any
+	var _data any
 
 	decoder := json.NewDecoder(rule)
 	err := decoder.Decode(&_rule)
@@ -525,8 +525,8 @@ func GetJsonLogicWithSolvedVars(rule, data json.RawMessage) ([]byte, error) {
 	}
 
 	// parse rule and data from json.RawMessage to interface
-	var _rule interface{}
-	var _data interface{}
+	var _rule any
+	var _data any
 
 	err := json.Unmarshal(rule, &_rule)
 	if err != nil {
@@ -548,8 +548,8 @@ func ApplyRaw(rule, data json.RawMessage) (json.RawMessage, error) {
 		data = json.RawMessage("{}")
 	}
 
-	var _rule interface{}
-	var _data interface{}
+	var _rule any
+	var _data any
 
 	err := json.Unmarshal(rule, &_rule)
 	if err != nil {
@@ -569,9 +569,9 @@ func ApplyRaw(rule, data json.RawMessage) (json.RawMessage, error) {
 	return json.Marshal(&result)
 }
 
-// ApplyInterface receives a rule and data as interface{} and returns the result
+// ApplyInterface receives a rule and data as any and returns the result
 // of the rule applied to the data.
-func ApplyInterface(rule, data interface{}) (output interface{}, err error) {
+func ApplyInterface(rule, data any) (output any, err error) {
 	defer func() {
 		if e := recover(); e != nil {
 			// fmt.Println("stacktrace from panic: \n" + string(debug.Stack()))
@@ -584,13 +584,13 @@ func ApplyInterface(rule, data interface{}) (output interface{}, err error) {
 	}
 
 	if isSlice(rule) {
-		var parsed []interface{}
+		var parsed []any
 
-		for _, value := range rule.([]interface{}) {
+		for _, value := range rule.([]any) {
 			parsed = append(parsed, parseValues(value, data))
 		}
 
-		return interface{}(parsed), nil
+		return any(parsed), nil
 	}
 
 	return rule, err
diff --git a/math.go b/math.go
index 81aad6e..7dc634f 100644
--- a/math.go
+++ b/math.go
@@ -2,30 +2,30 @@ package jsonlogic
 
 import "math"
 
-func mod(a interface{}, b interface{}) interface{} {
+func mod(a any, b any) any {
 	_a := toNumber(a)
 	_b := toNumber(b)
 
 	return math.Mod(_a, _b)
 }
 
-func abs(a interface{}) interface{} {
+func abs(a any) any {
 	_a := toNumber(a)
 
 	return math.Abs(_a)
 }
 
-func sum(values interface{}) interface{} {
+func sum(values any) any {
 	sum := float64(0)
 
-	for _, n := range values.([]interface{}) {
+	for _, n := range values.([]any) {
 		sum += toNumber(n)
 	}
 
 	return sum
 }
 
-func minus(values interface{}) interface{} {
+func minus(values any) any {
 	_values := toSliceOfNumbers(values)
 
 	sum := _values[0]
@@ -36,17 +36,17 @@ func minus(values interface{}) interface{} {
 	return sum
 }
 
-func mult(values interface{}) interface{} {
+func mult(values any) any {
 	sum := float64(1)
 
-	for _, n := range values.([]interface{}) {
+	for _, n := range values.([]any) {
 		sum *= toNumber(n)
 	}
 
 	return sum
 }
 
-func div(values interface{}) interface{} {
+func div(values any) any {
 	_values := toSliceOfNumbers(values)
 
 	sum := _values[0]
diff --git a/operation.go b/operation.go
index 4cf4271..40716b0 100644
--- a/operation.go
+++ b/operation.go
@@ -1,14 +1,14 @@
 package jsonlogic
 
 // customOperators holds custom operators
-var customOperators = make(map[string]func(values, data interface{}) (result interface{}))
+var customOperators = make(map[string]func(values, data any) (result any))
 
 // AddOperator allows for custom operators to be used
-func AddOperator(key string, cb func(values, data interface{}) (result interface{})) {
+func AddOperator(key string, cb func(values, data any) (result any)) {
 	customOperators[key] = cb
 }
 
-func operation(operator string, values, data interface{}) interface{} {
+func operation(operator string, values, data any) any {
 	// "AND" evaluates values lazily, so parseValues() is delayed until needed
 	if operator == "and" {
 		return _and(values, data)
@@ -77,7 +77,7 @@ func operation(operator string, values, data interface{}) interface{} {
 		return nil
 	}
 
-	parsed := values.([]interface{})
+	parsed := values.([]any)
 
 	if operator != "in" && len(parsed) == 1 {
 		return unary(operator, parsed[0])
diff --git a/readme.md b/readme.md
index 4064715..c904e81 100644
--- a/readme.md
+++ b/readme.md
@@ -119,7 +119,7 @@ import (
 
 func main() {
 	// add a new operator "strlen" for get string length
-	jsonlogic.AddOperator("strlen", func(values, data interface{}) interface{} {
+	jsonlogic.AddOperator("strlen", func(values, data any) any {
 		v, ok := values.(string)
 		if ok {
 			return len(v)
diff --git a/strings.go b/strings.go
index b4c0269..aa759fb 100644
--- a/strings.go
+++ b/strings.go
@@ -5,8 +5,8 @@ import (
 	"strings"
 )
 
-func substr(values interface{}) interface{} {
-	parsed := values.([]interface{})
+func substr(values any) any {
+	parsed := values.([]any)
 
 	runes := []rune(toString(parsed[0]))
 
@@ -41,13 +41,13 @@ func substr(values interface{}) interface{} {
 	return string(runes[from:to])
 }
 
-func concat(values interface{}) interface{} {
+func concat(values any) any {
 	if isString(values) {
 		return values
 	}
 
 	var s bytes.Buffer
-	for _, text := range values.([]interface{}) {
+	for _, text := range values.([]any) {
 		s.WriteString(toString(text))
 	}
 
diff --git a/validator.go b/validator.go
index 72ae2ae..5052cc4 100644
--- a/validator.go
+++ b/validator.go
@@ -7,7 +7,7 @@ import (
 
 // IsValid reads a JSON Logic rule from io.Reader and validates it
 func IsValid(rule io.Reader) bool {
-	var _rule interface{}
+	var _rule any
 
 	decoderRule := json.NewDecoder(rule)
 	err := decoderRule.Decode(&_rule)
@@ -18,13 +18,13 @@ func IsValid(rule io.Reader) bool {
 	return ValidateJsonLogic(_rule)
 }
 
-func ValidateJsonLogic(rules interface{}) bool {
+func ValidateJsonLogic(rules any) bool {
 	if isVar(rules) {
 		return true
 	}
 
 	if isMap(rules) {
-		for operator, value := range rules.(map[string]interface{}) {
+		for operator, value := range rules.(map[string]any) {
 			if !isOperator(operator) {
 				return false
 			}
@@ -34,7 +34,7 @@ func ValidateJsonLogic(rules interface{}) bool {
 	}
 
 	if isSlice(rules) {
-		for _, value := range rules.([]interface{}) {
+		for _, value := range rules.([]any) {
 			if isSlice(value) || isMap(value) {
 				if ValidateJsonLogic(value) {
 					continue
@@ -107,12 +107,12 @@ func isOperator(op string) bool {
 	return false
 }
 
-func isVar(value interface{}) bool {
+func isVar(value any) bool {
 	if !isMap(value) {
 		return false
 	}
 
-	_var, ok := value.(map[string]interface{})["var"]
+	_var, ok := value.(map[string]any)["var"]
 	if !ok {
 		return false
 	}
diff --git a/vars.go b/vars.go
index 10bf073..44ae962 100644
--- a/vars.go
+++ b/vars.go
@@ -6,11 +6,11 @@ import (
 	"strings"
 )
 
-func solveVars(values, data interface{}) interface{} {
+func solveVars(values, data any) any {
 	if isMap(values) {
-		logic := map[string]interface{}{}
+		logic := map[string]any{}
 
-		for key, value := range values.(map[string]interface{}) {
+		for key, value := range values.(map[string]any) {
 			if key == "var" {
 				if isString(value) && (value == "" || strings.HasPrefix(value.(string), ".")) {
 					logic["var"] = value
@@ -28,13 +28,13 @@ func solveVars(values, data interface{}) interface{} {
 			}
 		}
 
-		return interface{}(logic)
+		return any(logic)
 	}
 
 	if isSlice(values) {
-		logic := []interface{}{}
+		logic := []any{}
 
-		for _, value := range values.([]interface{}) {
+		for _, value := range values.([]any) {
 			logic = append(logic, solveVars(value, data))
 		}
 
@@ -44,7 +44,7 @@ func solveVars(values, data interface{}) interface{} {
 	return values
 }
 
-func getVar(value, data interface{}) interface{} {
+func getVar(value, data any) any {
 	if value == nil {
 		if !isPrimitive(data) {
 			return nil
@@ -60,10 +60,10 @@ func getVar(value, data interface{}) interface{} {
 		value = toString(value)
 	}
 
-	var _default interface{}
+	var _default any
 
 	if isSlice(value) { // syntax sugar
-		v := value.([]interface{})
+		v := value.([]any)
 
 		if len(v) == 0 {
 			return data
@@ -82,7 +82,7 @@ func getVar(value, data interface{}) interface{} {
 
 	parts := strings.Split(value.(string), ".")
 
-	var _value interface{}
+	var _value any
 
 	for _, part := range parts {
 		if part == "" {
@@ -90,12 +90,12 @@ func getVar(value, data interface{}) interface{} {
 		}
 
 		if isMap(data) {
-			_value = data.(map[string]interface{})[part]
+			_value = data.(map[string]any)[part]
 		}
 
 		if isSlice(data) {
 			pos := int(toNumber(part))
-			container := data.([]interface{})
+			container := data.([]any)
 			if pos >= len(container) {
 				return _default
 			}
@@ -116,9 +116,9 @@ func getVar(value, data interface{}) interface{} {
 	return _value
 }
 
-func solveVarsBackToJsonLogic(rule, data interface{}) (json.RawMessage, error) {
-	ruleMap := rule.(map[string]interface{})
-	result := make(map[string]interface{})
+func solveVarsBackToJsonLogic(rule, data any) (json.RawMessage, error) {
+	ruleMap := rule.(map[string]any)
+	result := make(map[string]any)
 
 	for operator, values := range ruleMap {
 		result[operator] = solveVars(values, data)


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index dce5a48..88c12e3 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -5,7 +5,7 @@ jobs:
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        go: ['1.16', '1.18', '1.19', '1.20', '1.21']
+        go: ['1.18', '1.19', '1.20', '1.21', '1.22', '1.23']
     name: Running with Go ${{ matrix.go }}
     steps:
     - name: Install Go
diff --git a/internal/testing.go b/internal/testing.go
index 7014a87..58bf5e1 100644
--- a/internal/testing.go
+++ b/internal/testing.go
@@ -19,7 +19,7 @@ type (
 	Tests []Test
 )
 
-func convertInterfaceToReader(i interface{}) io.Reader {
+func convertInterfaceToReader(i any) io.Reader {
 	var result bytes.Buffer
 
 	encoder := json.NewEncoder(&result)
@@ -45,7 +45,7 @@ func GetScenariosFromOfficialTestSuite() Tests {
 
 	response.Body.Close()
 
-	var scenarios []interface{}
+	var scenarios []any
 
 	err = json.Unmarshal(buffer, &scenarios)
 	if err != nil {
@@ -55,13 +55,13 @@ func GetScenariosFromOfficialTestSuite() Tests {
 	}
 
 	// add missing but relevant scenarios
-	var rule []interface{}
+	var rule []any
 
 	scenarios = append(scenarios,
 		append(rule,
-			make(map[string]interface{}),
-			make(map[string]interface{}),
-			make(map[string]interface{})))
+			make(map[string]any),
+			make(map[string]any),
+			make(map[string]any)))
 
 	for _, scenario := range scenarios {
 		if reflect.ValueOf(scenario).Kind() == reflect.String {
@@ -69,9 +69,9 @@ func GetScenariosFromOfficialTestSuite() Tests {
 		}
 
 		tests = append(tests, Test{
-			Rule:     convertInterfaceToReader(scenario.([]interface{})[0]),
-			Data:     convertInterfaceToReader(scenario.([]interface{})[1]),
-			Expected: convertInterfaceToReader(scenario.([]interface{})[2]),
+			Rule:     convertInterfaceToReader(scenario.([]any)[0]),
+			Data:     convertInterfaceToReader(scenario.([]any)[1]),
+			Expected: convertInterfaceToReader(scenario.([]any)[2]),
 		})
 	}
 
diff --git a/jsonlogic_test.go b/jsonlogic_test.go
index 44af119..90bc105 100644
--- a/jsonlogic_test.go
+++ b/jsonlogic_test.go
@@ -381,8 +381,8 @@ func TestDataWithDefaultValueWithApplyRaw(t *testing.T) {
 }
 
 func TestDataWithDefaultValueWithApplyInterface(t *testing.T) {
-	rule := map[string]interface{}{
-		"+": []interface{}{
+	rule := map[string]any{
+		"+": []any{
 			float64(1),
 			float64(2),
 		},
@@ -398,8 +398,8 @@ func TestDataWithDefaultValueWithApplyInterface(t *testing.T) {
 }
 
 func TestMissingOperators(t *testing.T) {
-	rule := map[string]interface{}{
-		"sum": []interface{}{
+	rule := map[string]any{
+		"sum": []any{
 			float64(1),
 			float64(2),
 		},
@@ -571,10 +571,10 @@ func TestReduceFilterAndNotContains(t *testing.T) {
 func TestReduceWithUnsupportedValue(t *testing.T) {
 	b := []byte(`{"reduce":[{"filter":[{"var":"data"},{"==":[{"var":""},""]}]},{"cat":[{"var":"current"},{"var":"accumulator"}]},null]}`)
 
-	rule := map[string]interface{}{}
+	rule := map[string]any{}
 	_ = json.Unmarshal(b, &rule)
-	data := map[string]interface{}{
-		"data": []interface{}{"str"},
+	data := map[string]any{
+		"data": []any{"str"},
 	}
 
 	_, err := jsonlogic.ApplyInterface(rule, data)
@@ -582,7 +582,7 @@ func TestReduceWithUnsupportedValue(t *testing.T) {
 }
 
 func TestAddOperator(t *testing.T) {
-	jsonlogic.AddOperator("strlen", func(values, data interface{}) interface{} {
+	jsonlogic.AddOperator("strlen", func(values, data any) any {
 		v, ok := values.(string)
 
 		if ok {