diff --git a/.changeset/serious-suns-drive.md b/.changeset/serious-suns-drive.md
new file mode 100644
index 0000000000..c689b16407
--- /dev/null
+++ b/.changeset/serious-suns-drive.md
@@ -0,0 +1,6 @@
+---
+"type-plus": minor
+---
+
+Add `Assignable`.
+Deprecated `CanAssign` and `StrictCanAssign`.
diff --git a/type-plus/ts/index.ts b/type-plus/ts/index.ts
index 91a71c45df..fe82e22b17 100644
--- a/type-plus/ts/index.ts
+++ b/type-plus/ts/index.ts
@@ -86,6 +86,7 @@ export type * from './object/is_not_strict_object.js'
export type * from './object/is_object.js'
export type * from './object/is_strict_object.js'
export * as ObjectPlus from './object/object_plus.js'
+export type * from './predicates/assignable.js'
export * from './predicates/index.js'
export type { PrimitiveTypes } from './primitive.js'
export * from './promise/index.js'
@@ -121,8 +122,8 @@ export type * from './type_plus/branch/$input_options.js'
export type * from './type_plus/branch/$is_distributive.js'
export type * from './type_plus/branch/$resolve_branch.js'
export type * from './type_plus/branch/$select.js'
-export type * from './type_plus/branch/$selection_options.js'
export type * from './type_plus/branch/$selection.js'
+export type * from './type_plus/branch/$selection_options.js'
export type * from './undefined/has_undefined.js'
export type * from './undefined/is_not_undefined.js'
export type * from './undefined/is_undefined.js'
diff --git a/type-plus/ts/predicates/CanAssign.ts b/type-plus/ts/predicates/CanAssign.ts
index 1073061833..65d31d9249 100644
--- a/type-plus/ts/predicates/CanAssign.ts
+++ b/type-plus/ts/predicates/CanAssign.ts
@@ -1,5 +1,5 @@
import type { NotExtendable } from './Extends.js'
-import type { IsEmptyObject } from './IsEmptyObject.js'
+import type { Assignable } from './assignable.js'
/**
* Can `A` assign to `B`
@@ -10,6 +10,8 @@ import type { IsEmptyObject } from './IsEmptyObject.js'
*
* This is the correct behavior.
*
+ * @deprecated use `Assignable` instead
+ *
* @example
* ```ts
* CanAssign // boolean
@@ -37,23 +39,15 @@ export type CanAssign = boolean extends A
*
* All branches in an union `A` are assignable to `B`.
*
+ * @deprecated use `Assignable // false
* StrictCanAssign // true
* ```
*/
-export type StrictCanAssign = IsEmptyObject extends true
- ? Record extends B
- ? Then
- : Else
- : boolean extends A
- ? boolean extends B
- ? Then
- : Else
- : [A] extends [B]
- ? Then
- : Else
+export type StrictCanAssign = Assignable
/**
* @deprecated use `CanAssign` instead
diff --git a/type-plus/ts/predicates/Extends.ts b/type-plus/ts/predicates/Extends.ts
index 0e35d90b8f..2968cc425f 100644
--- a/type-plus/ts/predicates/Extends.ts
+++ b/type-plus/ts/predicates/Extends.ts
@@ -1,3 +1,6 @@
+/**
+ * @deprecated use `$Assignable`
+ */
export type Extendable = A extends B ? Then : Else
export type NotExtendable = A extends B ? Else : Then
export type IsExtend = A extends B ? Then : Else
diff --git a/type-plus/ts/predicates/assignable.spec.ts b/type-plus/ts/predicates/assignable.spec.ts
new file mode 100644
index 0000000000..57d1892710
--- /dev/null
+++ b/type-plus/ts/predicates/assignable.spec.ts
@@ -0,0 +1,113 @@
+import { it } from '@jest/globals'
+import { testType, type Assignable, type $Then, type $Else } from '../index.js'
+
+it('check if A can be assigned to B', () => {
+ testType.true>(true)
+ testType.true>(true)
+ testType.true>(true)
+ testType.true>(true)
+ testType.true>(true)
+ testType.true>(true)
+ testType.true>(true)
+ testType.true>(true)
+ testType.true>(true)
+ testType.true>(true)
+ testType.true>(true)
+
+ testType.false>(true)
+ testType.false>(true)
+ testType.false>(true)
+ testType.false>(true)
+})
+
+it('returns true when B is `any` as anything can be assigned to `any`', () => {
+ testType.true>(true)
+ testType.true>(true)
+ testType.true>(true)
+
+ testType.true>(true)
+ testType.true>(true)
+ testType.true>(true)
+})
+
+it('returns true when B is `unknown` as anything can be assigned to `unknown`', () => {
+ testType.true>(true)
+ testType.true>(true)
+ testType.true>(true)
+
+ testType.true>(true)
+ testType.true>(true)
+ testType.true>(true)
+})
+
+it('returns false when B is `never` except when A is `never`', () => {
+ testType.false>(true)
+ testType.false>(true)
+ testType.true>(true)
+
+ testType.false>(true)
+ testType.false>(true)
+ testType.false>(true)
+})
+
+it('works against special types', () => {
+ testType.equal, true>(true)
+ testType.equal, true>(true)
+ testType.equal, true>(true)
+})
+
+it('can disable distribution', () => {
+ testType.equal, boolean>(true)
+ testType.equal, false>(true)
+
+ testType.equal, boolean>(true)
+ testType.equal, false>(true)
+})
+
+it('can use as filter', () => {
+ testType.equal, 1>(true)
+ testType.never>(true)
+})
+
+it('work as branching', () => {
+ testType.equal, $Then>(true)
+ testType.equal, $Then>(true)
+ testType.equal, $Else>(true)
+ testType.equal, $Then>(true)
+ testType.equal, $Then>(true)
+ testType.equal, $Else>(true)
+})
+
+it('works with partial customization', () => {
+ testType.equal, 1>(true)
+ testType.equal, 1>(true)
+ testType.equal, 1>(true)
+ testType.equal, 1>(true)
+ testType.equal, 2>(true)
+ testType.equal, 2>(true)
+
+ testType.equal, true>(true)
+ testType.equal, true>(true)
+ testType.equal, true>(true)
+ testType.equal, true>(true)
+ testType.equal, false>(true)
+ testType.equal, false>(true)
+})
+
+it('can override $any branch', () => {
+ testType.equal, true>(true)
+ testType.equal, unknown>(true)
+ testType.equal, unknown>(true)
+})
+
+it('can override $unknown branch', () => {
+ testType.equal, true>(true)
+ testType.equal, unknown>(true)
+ testType.equal, unknown>(true)
+})
+
+it('can override $never branch', () => {
+ testType.equal, true>(true)
+ testType.equal, unknown>(true)
+ testType.equal, unknown>(true)
+})
diff --git a/type-plus/ts/predicates/assignable.ts b/type-plus/ts/predicates/assignable.ts
new file mode 100644
index 0000000000..689f8008ad
--- /dev/null
+++ b/type-plus/ts/predicates/assignable.ts
@@ -0,0 +1,94 @@
+import type { $Any } from '../any/any.js'
+import type { $Never } from '../never/never.js'
+import type { $SpecialType } from '../type_plus/$special_type.js'
+import type { $DistributiveDefault, $DistributiveOptions } from '../type_plus/branch/$distributive.js'
+import type { $InputOptions } from '../type_plus/branch/$input_options.js'
+import type { $IsDistributive } from '../type_plus/branch/$is_distributive.js'
+import type { $ResolveBranch } from '../type_plus/branch/$resolve_branch.js'
+import type { $Else, $SelectionBranch, $SelectionPredicate, $Then } from '../type_plus/branch/$selection.js'
+import type { $SelectionOptions } from '../type_plus/branch/$selection_options.js'
+import type { $Unknown } from '../unknown/unknown.js'
+
+/**
+ * 🧰 *tool utils*
+ *
+ * Validate if `A` is assignable to `B`.
+ *
+ * @example
+ * ```ts
+ * type R = Assignable // true
+ * type R = Assignable // true
+ * type R = Assignable // true
+ * type R = Assignable // true
+ * type R = Assignable<1, 1> // true
+ * type R = Assignable<'a', 'a'> // true
+ * type R = Assignable<'a', 'b'> // false
+ * type R = Assignable<'a', string> // true
+ * ```
+ *
+ * 🔢 *customize*
+ *
+ * Filter to ensure `A` is assignable to `B`.
+ *
+ * @example
+ * ```ts
+ * type R = Assignable // any
+ * type R = Assignable<1, number, { selection: 'filter' }> // 1
+ * ```
+ *
+ * 🔢 *customize*
+ *
+ * Use unique branch identifiers to allow precise processing of the result.
+ *
+ * @example
+ * ```ts
+ * type R = Assignable // $Then
+ * ```
+ *
+ * 🔢 *customize*
+ *
+ * Override special types branch.
+ *
+ * @example
+ * ```ts
+ * type R = Assignable // 1
+ * type R = Assignable // 1
+ * type R = Assignable // 1
+ * ```
+ */
+export type Assignable<
+ A,
+ B,
+ $O extends Assignable.$Options = {}
+> = $SpecialType,
+ $unknown: $ResolveBranch,
+ $never: $ResolveBranch,
+ $else: $SpecialType,
+ $unknown: $ResolveBranch,
+ $never: $ResolveBranch,
+ $else: Assignable.$
+ }>
+}>
+
+
+export namespace Assignable {
+ export type $Options = $SelectionOptions & $DistributiveOptions & $InputOptions<$Any | $Unknown | $Never>
+ export type $Default = $SelectionPredicate & $DistributiveDefault
+ export type $Branch = $SelectionBranch & $DistributiveDefault
+
+ /**
+ * 🧰 *type util*
+ *
+ *
+ */
+ export type $<
+ A,
+ B,
+ $O extends Assignable.$Options = {}
+ > = $IsDistributive<$O, {
+ $then: $ResolveBranch,
+ $else: $ResolveBranch
+ }>
+}
diff --git a/type-plus/ts/predicates/index.ts b/type-plus/ts/predicates/index.ts
index cbdae9010c..38dbbb13a2 100644
--- a/type-plus/ts/predicates/index.ts
+++ b/type-plus/ts/predicates/index.ts
@@ -4,4 +4,3 @@ export type { If } from './If.js'
export type { IsEmptyObject } from './IsEmptyObject.js'
export type { IsLiteral } from './literal.js'
export type { And, Not, Or, Xor } from './logical.js'
-