Use type constraints for assertions
diff --git a/assert/assert.go b/assert/assert.go
index bb3e0e0..1451623 100644
--- a/assert/assert.go
+++ b/assert/assert.go
@@ -199,7 +199,7 @@
 // called from the goroutine running the test function, not from other
 // goroutines created during the test. Use Check with cmp.Equal from other
 // goroutines.
-func Equal(t TestingT, x, y interface{}, msgAndArgs ...any) {
+func Equal[ANY any](t TestingT, x, y ANY, msgAndArgs ...any) {
 	if ht, ok := t.(helperT); ok {
 		ht.Helper()
 	}
@@ -218,7 +218,7 @@
 // called from the goroutine running the test function, not from other
 // goroutines created during the test. Use Check with cmp.DeepEqual from other
 // goroutines.
-func DeepEqual(t TestingT, x, y interface{}, opts ...gocmp.Option) {
+func DeepEqual[ANY any](t TestingT, x, y ANY, opts ...gocmp.Option) {
 	if ht, ok := t.(helperT); ok {
 		ht.Helper()
 	}
diff --git a/assert/assert_test.go b/assert/assert_test.go
index 3b13abb..5f7f6fa 100644
--- a/assert/assert_test.go
+++ b/assert/assert_test.go
@@ -283,13 +283,6 @@
 	expectFailNowed(t, fakeT, "assertion failed: 1 (actual int) != 3 (expected int)")
 }
 
-func TestEqualFailureTypes(t *testing.T) {
-	fakeT := &fakeTestingT{}
-
-	Equal(fakeT, 3, uint(3))
-	expectFailNowed(t, fakeT, `assertion failed: 3 (int) != 3 (uint)`)
-}
-
 func TestEqualFailureWithSelectorArgument(t *testing.T) {
 	fakeT := &fakeTestingT{}
 
diff --git a/assert/cmp/compare.go b/assert/cmp/compare.go
index 2bb9e8e..c59ff0b 100644
--- a/assert/cmp/compare.go
+++ b/assert/cmp/compare.go
@@ -24,7 +24,7 @@
 // The comparison can be customized using comparison Options.
 // Package http://pkg.go.dev/gotest.tools/v3/assert/opt provides some additional
 // commonly used Options.
-func DeepEqual(x, y interface{}, opts ...cmp.Option) Comparison {
+func DeepEqual[ANY any](x, y ANY, opts ...cmp.Option) Comparison {
 	return func() (result Result) {
 		defer func() {
 			if panicmsg, handled := handleCmpPanic(recover()); handled {
@@ -63,7 +63,9 @@
 
 // RegexOrPattern may be either a *regexp.Regexp or a string that is a valid
 // regexp pattern.
-type RegexOrPattern interface{}
+type RegexOrPattern interface {
+	~string | *regexp.Regexp
+}
 
 // Regexp succeeds if value v matches regular expression re.
 //
@@ -72,7 +74,7 @@
 //	assert.Assert(t, cmp.Regexp("^[0-9a-f]{32}$", str))
 //	r := regexp.MustCompile("^[0-9a-f]{32}$")
 //	assert.Assert(t, cmp.Regexp(r, str))
-func Regexp(re RegexOrPattern, v string) Comparison {
+func Regexp[R RegexOrPattern](re R, v string) Comparison {
 	match := func(re *regexp.Regexp) Result {
 		return toResult(
 			re.MatchString(v),
@@ -80,7 +82,7 @@
 	}
 
 	return func() Result {
-		switch regex := re.(type) {
+		switch regex := any(re).(type) {
 		case *regexp.Regexp:
 			return match(regex)
 		case string:
@@ -96,13 +98,13 @@
 }
 
 // Equal succeeds if x == y. See assert.Equal for full documentation.
-func Equal(x, y interface{}) Comparison {
+func Equal[ANY any](x, y ANY) Comparison {
 	return func() Result {
 		switch {
-		case x == y:
+		case any(x) == any(y):
 			return ResultSuccess
 		case isMultiLineStringCompare(x, y):
-			diff := format.UnifiedDiff(format.DiffConfig{A: x.(string), B: y.(string)})
+			diff := format.UnifiedDiff(format.DiffConfig{A: any(x).(string), B: any(y).(string)})
 			return multiLineDiffResult(diff, x, y)
 		}
 		return ResultFailureTemplate(`
@@ -117,7 +119,7 @@
 	}
 }
 
-func isMultiLineStringCompare(x, y interface{}) bool {
+func isMultiLineStringCompare(x, y any) bool {
 	strX, ok := x.(string)
 	if !ok {
 		return false
@@ -129,7 +131,7 @@
 	return strings.Contains(strX, "\n") || strings.Contains(strY, "\n")
 }
 
-func multiLineDiffResult(diff string, x, y interface{}) Result {
+func multiLineDiffResult(diff string, x, y any) Result {
 	return ResultFailureTemplate(`
 --- {{ with callArg 0 }}{{ formatNode . }}{{else}}←{{end}}
 +++ {{ with callArg 1 }}{{ formatNode . }}{{else}}→{{end}}
@@ -138,7 +140,7 @@
 }
 
 // Len succeeds if the sequence has the expected length.
-func Len(seq interface{}, expected int) Comparison {
+func Len(seq any, expected int) Comparison {
 	return func() (result Result) {
 		defer func() {
 			if e := recover(); e != nil {
@@ -163,7 +165,7 @@
 // If collection is a Map, contains will succeed if item is a key in the map.
 // If collection is a slice or array, item is compared to each item in the
 // sequence using reflect.DeepEqual().
-func Contains(collection interface{}, item interface{}) Comparison {
+func Contains(collection any, item any) Comparison {
 	return func() Result {
 		colValue := reflect.ValueOf(collection)
 		if !colValue.IsValid() {
@@ -261,14 +263,14 @@
 //
 // Use NilError() for comparing errors. Use Len(obj, 0) for comparing slices,
 // maps, and channels.
-func Nil(obj interface{}) Comparison {
+func Nil(obj any) Comparison {
 	msgFunc := func(value reflect.Value) string {
 		return fmt.Sprintf("%v (type %s) is not nil", reflect.Indirect(value), value.Type())
 	}
 	return isNil(obj, msgFunc)
 }
 
-func isNil(obj interface{}, msgFunc func(reflect.Value) string) Comparison {
+func isNil(obj any, msgFunc func(reflect.Value) string) Comparison {
 	return func() Result {
 		if obj == nil {
 			return ResultSuccess
@@ -309,7 +311,7 @@
 // Fails if err does not implement the reflect.Type.
 //
 // Deprecated: Use ErrorIs
-func ErrorType(err error, expected interface{}) Comparison {
+func ErrorType(err error, expected any) Comparison {
 	return func() Result {
 		switch expectedType := expected.(type) {
 		case func(error) bool:
diff --git a/assert/cmp/compare_test.go b/assert/cmp/compare_test.go
index e4546ea..b315a21 100644
--- a/assert/cmp/compare_test.go
+++ b/assert/cmp/compare_test.go
@@ -45,13 +45,15 @@
 }
 
 func TestRegexp(t *testing.T) {
-	var testcases = []struct {
+	type testCase struct {
 		name   string
-		regex  interface{}
+		regex  string
 		value  string
 		match  bool
 		expErr string
-	}{
+	}
+
+	var testcases = []testCase{
 		{
 			name:  "pattern string match",
 			regex: "^[0-9]+$",
@@ -71,23 +73,11 @@
 			expErr: `value "2123423456" does not match regexp "^1"`,
 		},
 		{
-			name:  "regexp match",
-			regex: regexp.MustCompile("^d[0-9a-f]{8}$"),
-			value: "d1632beef",
-			match: true,
-		},
-		{
 			name:   "invalid regexp",
 			regex:  "^1(",
 			value:  "2",
 			expErr: "error parsing regexp: missing closing ): `^1(`",
 		},
-		{
-			name:   "invalid type",
-			regex:  struct{}{},
-			value:  "some string",
-			expErr: "invalid type struct {} for regex pattern",
-		},
 	}
 
 	for _, tc := range testcases {
@@ -100,6 +90,12 @@
 			}
 		})
 	}
+
+	t.Run("regexp match", func(t *testing.T) {
+		regex := regexp.MustCompile("^d[0-9a-f]{8}$")
+		res := Regexp(regex, "d1632beef")()
+		assertSuccess(t, res)
+	})
 }
 
 func TestLen(t *testing.T) {
diff --git a/fs/example_test.go b/fs/example_test.go
index ece5b4b..1ec61f5 100644
--- a/fs/example_test.go
+++ b/fs/example_test.go
@@ -29,7 +29,7 @@
 
 	content, err := os.ReadFile(file.Path())
 	assert.NilError(t, err)
-	assert.Equal(t, "content\n", content)
+	assert.Equal(t, "content\n", string(content))
 }
 
 // Create a directory and subdirectory with files