is.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. import type { TresCamera, TresInstance, TresObject, TresPrimitive } from 'src/types'
  2. import type { BufferGeometry, Color, ColorRepresentation, Fog, Light, Material, Object3D, OrthographicCamera, PerspectiveCamera, Scene } from 'three'
  3. import { Layers } from 'three'
  4. /**
  5. * Type guard to check if a value is undefined
  6. * @param value - The value to check
  7. * @returns True if the value is undefined, false otherwise
  8. * @example
  9. * ```ts
  10. * const value = undefined
  11. * if (isUndefined(value)) {
  12. * // TypeScript knows value is undefined here
  13. * }
  14. * ```
  15. */
  16. export function isUndefined(value: unknown): value is undefined {
  17. return typeof value === 'undefined'
  18. }
  19. /**
  20. * Type guard to check if a value is an array
  21. * @param value - The value to check
  22. * @returns True if the value is an array, false otherwise
  23. * @example
  24. * ```ts
  25. * const value = [1, 2, 3]
  26. * if (isArray(value)) {
  27. * // TypeScript knows value is Array<unknown> here
  28. * value.length // OK
  29. * value.map(x => x) // OK
  30. * }
  31. * ```
  32. */
  33. export function isArray(value: unknown): value is Array<unknown> {
  34. return Array.isArray(value)
  35. }
  36. /**
  37. * Type guard to check if a value is a number
  38. * @param value - The value to check
  39. * @returns True if the value is a number (including NaN and Infinity), false otherwise
  40. * @example
  41. * ```ts
  42. * const value = 42
  43. * if (isNumber(value)) {
  44. * // TypeScript knows value is number here
  45. * value.toFixed(2) // OK
  46. * value * 2 // OK
  47. * }
  48. * ```
  49. */
  50. export function isNumber(value: unknown): value is number {
  51. return typeof value === 'number'
  52. }
  53. /**
  54. * Type guard to check if a value is a string
  55. * @param value - The value to check
  56. * @returns True if the value is a string, false otherwise
  57. * @example
  58. * ```ts
  59. * const value = "hello"
  60. * if (isString(value)) {
  61. * // TypeScript knows value is string here
  62. * value.length // OK
  63. * value.toUpperCase() // OK
  64. * }
  65. * ```
  66. */
  67. export function isString(value: unknown): value is string {
  68. return typeof value === 'string'
  69. }
  70. /**
  71. * Type guard to check if a value is a boolean
  72. * @param value - The value to check
  73. * @returns True if the value is strictly true or false, false otherwise
  74. * @example
  75. * ```ts
  76. * const value = true
  77. * if (isBoolean(value)) {
  78. * // TypeScript knows value is boolean here
  79. * !value // OK
  80. * value && true // OK
  81. * }
  82. * ```
  83. */
  84. export function isBoolean(value: unknown): value is boolean {
  85. return value === true || value === false
  86. }
  87. /**
  88. * Type guard to check if a value is a function
  89. * @param value - The value to check
  90. * @returns True if the value is a callable function, false otherwise
  91. * @example
  92. * ```ts
  93. * const value = () => {}
  94. * if (isFunction(value)) {
  95. * // TypeScript knows value is (...args: any[]) => any here
  96. * value() // OK
  97. * value.call(null) // OK
  98. * }
  99. * ```
  100. */
  101. export function isFunction(value: unknown): value is (...args: any[]) => any {
  102. return typeof value === 'function'
  103. }
  104. /**
  105. * Type guard to check if a value is a plain object
  106. * @param value - The value to check
  107. * @returns True if the value is a plain object (not null, array, or function), false otherwise
  108. * @example
  109. * ```ts
  110. * const value = { key: 'value' }
  111. * if (isObject(value)) {
  112. * // TypeScript knows value is Record<string | number | symbol, unknown> here
  113. * Object.keys(value) // OK
  114. * value.key // OK
  115. * }
  116. * ```
  117. */
  118. export function isObject(value: unknown): value is Record<string | number | symbol, unknown> {
  119. return value === Object(value) && !isArray(value) && !isFunction(value)
  120. }
  121. /**
  122. * Type guard to check if a value is a Three.js Object3D
  123. * @param value - The value to check
  124. * @returns True if the value is a Three.js Object3D instance, false otherwise
  125. * @example
  126. * ```ts
  127. * const value = new THREE.Object3D()
  128. * if (isObject3D(value)) {
  129. * // TypeScript knows value is Object3D here
  130. * value.position // OK
  131. * value.rotation // OK
  132. * value.scale // OK
  133. * }
  134. * ```
  135. */
  136. export function isObject3D(value: unknown): value is Object3D {
  137. return isObject(value) && !!(value.isObject3D)
  138. }
  139. /**
  140. * Type guard to check if a value is a Three.js Camera
  141. * @param value - The value to check
  142. * @returns True if the value is a Three.js Camera instance, false otherwise
  143. * @example
  144. * ```ts
  145. * const value = new THREE.PerspectiveCamera()
  146. * if (isCamera(value)) {
  147. * // TypeScript knows value is Camera here
  148. * value.fov // OK
  149. * value.near // OK
  150. * value.far // OK
  151. * }
  152. * ```
  153. */
  154. export function isCamera(value: unknown): value is TresCamera {
  155. return isObject(value) && !!(value.isCamera)
  156. }
  157. export function isColor(value: unknown): value is Color {
  158. return isObject(value) && !!(value.isColor)
  159. }
  160. export function isColorRepresentation(value: unknown): value is ColorRepresentation {
  161. return value != null && (typeof value === 'string' || typeof value === 'number' || isColor(value))
  162. }
  163. interface VectorLike { set: (...args: any[]) => void, constructor?: (...args: any[]) => any }
  164. export function isVectorLike(value: unknown): value is VectorLike {
  165. return value !== null && typeof value === 'object' && 'set' in value && typeof value.set === 'function'
  166. }
  167. interface Copyable { copy: (...args: any[]) => void, constructor?: (...args: any[]) => any }
  168. export function isCopyable(value: unknown): value is Copyable {
  169. return isVectorLike(value) && 'copy' in value && typeof value.copy === 'function'
  170. }
  171. interface ClassInstance { constructor?: (...args: any[]) => any }
  172. export function isClassInstance(object: unknown): object is ClassInstance {
  173. return !!(object)?.constructor
  174. }
  175. export function isLayers(value: unknown): value is Layers {
  176. return value instanceof Layers // three does not implement .isLayers
  177. }
  178. /**
  179. * Type guard to check if a value is a Three.js OrthographicCamera
  180. * @param value - The value to check
  181. * @returns True if the value is a Three.js OrthographicCamera instance, false otherwise
  182. */
  183. export function isOrthographicCamera(value: unknown): value is OrthographicCamera {
  184. return isObject(value) && !!(value.isOrthographicCamera)
  185. }
  186. /**
  187. * Type guard to check if a value is a Three.js PerspectiveCamera
  188. * @param value - The value to check
  189. * @returns True if the value is a Three.js PerspectiveCamera instance, false otherwise
  190. */
  191. export function isPerspectiveCamera(value: unknown): value is PerspectiveCamera {
  192. return isObject(value) && !!(value.isPerspectiveCamera)
  193. }
  194. /**
  195. * Type guard to check if a value is a Three.js BufferGeometry
  196. * @param value - The value to check
  197. * @returns True if the value is a Three.js BufferGeometry instance, false otherwise
  198. * @example
  199. * ```ts
  200. * const value = new THREE.BufferGeometry()
  201. * if (isBufferGeometry(value)) {
  202. * // TypeScript knows value is BufferGeometry here
  203. * value.attributes // OK
  204. * value.index // OK
  205. * value.computeVertexNormals() // OK
  206. * }
  207. * ```
  208. */
  209. export function isBufferGeometry(value: unknown): value is BufferGeometry {
  210. return isObject(value) && !!(value.isBufferGeometry)
  211. }
  212. /**
  213. * Type guard to check if a value is a Three.js Material
  214. * @param value - The value to check
  215. * @returns True if the value is a Three.js Material instance, false otherwise
  216. * @example
  217. * ```ts
  218. * const value = new THREE.MeshStandardMaterial()
  219. * if (isMaterial(value)) {
  220. * // TypeScript knows value is Material here
  221. * value.color // OK
  222. * value.metalness // OK
  223. * value.roughness // OK
  224. * }
  225. * ```
  226. */
  227. export function isMaterial(value: unknown): value is Material {
  228. return isObject(value) && !!(value.isMaterial)
  229. }
  230. /**
  231. * Type guard to check if a value is a Three.js Light
  232. * @param value - The value to check
  233. * @returns True if the value is a Three.js Light instance, false otherwise
  234. * @example
  235. * ```ts
  236. * const value = new THREE.DirectionalLight()
  237. * if (isLight(value)) {
  238. * // TypeScript knows value is Light here
  239. * value.intensity // OK
  240. * value.color // OK
  241. * value.position // OK
  242. * }
  243. * ```
  244. */
  245. export function isLight(value: unknown): value is Light {
  246. return isObject(value) && !!(value.isLight)
  247. }
  248. /**
  249. * Type guard to check if a value is a Three.js Fog
  250. * @param value - The value to check
  251. * @returns True if the value is a Three.js Fog instance, false otherwise
  252. * @example
  253. * ```ts
  254. * const value = new THREE.Fog(0x000000, 1, 1000)
  255. * if (isFog(value)) {
  256. * // TypeScript knows value is Fog here
  257. * value.color // OK
  258. * value.near // OK
  259. * value.far // OK
  260. * }
  261. * ```
  262. */
  263. export function isFog(value: unknown): value is Fog {
  264. return isObject(value) && !!(value.isFog)
  265. }
  266. /**
  267. * Type guard to check if a value is a Three.js Scene
  268. * @param value - The value to check
  269. * @returns True if the value is a Three.js Scene instance, false otherwise
  270. * @example
  271. * ```ts
  272. * const value = new THREE.Scene()
  273. * if (isScene(value)) {
  274. * // TypeScript knows value is Scene here
  275. * value.children // OK
  276. * value.add(new THREE.Object3D()) // OK
  277. * value.remove(new THREE.Object3D()) // OK
  278. * }
  279. * ```
  280. */
  281. export function isScene(value: unknown): value is Scene {
  282. return isObject(value) && !!(value.isScene)
  283. }
  284. /**
  285. * Type guard to check if a value is a TresObject
  286. * @param value - The value to check
  287. * @returns True if the value is a TresObject (Object3D | BufferGeometry | Material | Fog), false otherwise
  288. * @example
  289. * ```ts
  290. * const value = new THREE.Mesh()
  291. * if (isTresObject(value)) {
  292. * // TypeScript knows value is TresObject here
  293. * // You can use common properties and methods shared by all TresObjects
  294. * }
  295. * ```
  296. * @remarks
  297. * TresObject is a union type that represents the core Three.js objects that can be used in TresJS.
  298. * This includes Object3D, BufferGeometry, Material, and Fog instances.
  299. */
  300. export function isTresObject(value: unknown): value is TresObject {
  301. // NOTE: TresObject is currently defined as
  302. // TresObject3D | THREE.BufferGeometry | THREE.Material | THREE.Fog
  303. return isObject3D(value) || isBufferGeometry(value) || isMaterial(value) || isFog(value)
  304. }
  305. /**
  306. * Type guard to check if a value is a TresPrimitive
  307. * @param value - The value to check
  308. * @returns True if the value is a TresPrimitive instance, false otherwise
  309. * @example
  310. * ```ts
  311. * const value = { isPrimitive: true }
  312. * if (isTresPrimitive(value)) {
  313. * // TypeScript knows value is TresPrimitive here
  314. * // You can use properties and methods specific to TresPrimitives
  315. * }
  316. * ```
  317. * @remarks
  318. * TresPrimitive is a special type in TresJS that represents primitive objects
  319. * that can be used directly in the scene without needing to be wrapped in a Three.js object.
  320. */
  321. export function isTresPrimitive(value: unknown): value is TresPrimitive {
  322. return isObject(value) && !!(value.isPrimitive)
  323. }
  324. /**
  325. * Type guard to check if a value is a TresInstance (has __tres property)
  326. * @param value - The value to check
  327. * @returns True if the value is a TresInstance (has __tres property), false otherwise
  328. * @example
  329. * ```ts
  330. * const value = new THREE.Mesh()
  331. * if (isTresInstance(value)) {
  332. * // TypeScript knows value is TresInstance here
  333. * // You can safely access value.__tres
  334. * }
  335. * ```
  336. */
  337. export function isTresInstance(value: unknown): value is TresInstance {
  338. return isTresObject(value) && '__tres' in value
  339. }