Header image

TypeScript – How To Avoid “Any”?

26/09/2022

629

  • The harmful effects of any
  • Avoiding any
TypeScript - How To Avoid "Any"?

How to avoid any?

In the previous blog – Typescript and “any” type, I introduced TypeScript and what exactly is any type.

In this blog, I’d like to show more about the harmful effects when using any and introduce some built-in types, features and customs types that you can use to avoid any.

The harmful effects of any

While TypeScript is a type checker, any type tells TypeScript to skip/disable type-checking. On the other hand, due to the nature of JavaScript, in some cases providing accurate types isn’t a simple task. In such situations, programmers are tempted to use any.

In most situations using or implicit any – a type that allows to store anything and skip type checkers, programmers can’t guarantee what type the value is stored and how it will be used. Furthermore, when the code is executed at runtime, errors may occur even though they were not warned before. For example:

let result; // Variable 'result' implicitly has an 'any' type.
result = 10.123; // Number is stored at 'result'

console.log(result.toFixed()); // `toFixed()` is a method of `number`

result.willExist(); // `willExist()` isn't a method of `number`, but no errors appear.

Because of that, the use of any is something that should be minimized as much as possible, to ensure the source code does not encounter any errors.

Avoiding any

Based on the basics of TypeScript and Everyday Types, in this blog, I’ll be sharing what I learned and used to write code without any.

Type aliases & Interfaces

A type alias is exactly a name for any type, you can actually use a type alias to give a name to any type at all, not just an object type. For example:

// Type alias
type Point = {
  x: number,
  y: number
};

type ID = number | string;

An interface declaration is another way to name an object type:

// Interface
interface IPoint {
  x: number,
  y: number
};

Differences between Type Aliases and Interfaces:

// Override
type Point = { // TypeError: Duplicate identifier 'Point'.
  a: string
};
interface IPoint {
  a: string
};

Union & Literal types

A union type is a type formed from two or more other types, representing values that may be any one of those types.

// Union types
let anyNumber: string | number;

// Usage
anyNumber = '123';
anyNumber = 123;
anyNumber = true; // TypeError: Type 'boolean' is not assignable to type 'string | number'.

In addition to the general types of string and number, you can refer to specific value of strings and numbers.
By combining literals into unions, you can express a much more useful concept. For example:

// Literal types
let direction: 'top' | 'left' | 'right' | 'bottom';

direction = 'top';
direction = 'top-right'; // TypeError: Type '"top-right"' is not assignable to type '"top" | "left" | "right" | "bottom"'

Type assertions

Sometimes you will have information about the type of a value that TypeScript can’t know about.

For example, if you’re using document.getElementById, TypeScript only knows that this will return some kind of HTMLElement, but you might know that your page will always have an HTMLCanvasElement with a given ID.

In this situation, you can use a type assertion to specify a more specific type:

// Type assertions
const myCanvas = document.getElementById('main-canvas') as HTMLCanvasElement;

Generics

// Example
const getRandomNumber = (items: number[]): number => {
  let randomIndex = Math.floor(Math.random() * items.length);
  return items[randomIndex];
};
const getRandomString = (items: string[]): string => {
  let randomIndex = Math.floor(Math.random() * items.length);
  return items[randomIndex];
};

// Generics function
const getRandomGeneric = <T>(items: T[]): T => {
  let randomIndex = Math.floor(Math.random() * items.length);
  return items[randomIndex];
};

// Usage
const teams: string[] = ['frontend', 'ios', 'android'];
const numbers: number[] = [1, 2, 3, 4, 5, 6, 7, 9, 10];

const randomResult1 = getRandomGeneric<string>(teams);
const randomResult2 = getRandomGeneric<number>(numbers);

In the example above, the getRandomGeneric is the generic identity function that worked over a range of types.

The type of generic functions is just like those of non-generic functions, with the type parameters listed first, similarly to function declarations:

const identity = <Type>(param: Type): Type => {
  return param;
};

When calling identity a function, you now will also need to specify the type of param that the function will use.

The detail above just Generic identity functions, you can read more generics Generic link

Unknown

unknown is what should be used when you don’t know a proper type of object. Unlike any, it doesn’t let you do any operations on a value until you know its type (skip/disable type-checker).

When you unknow something, you need to check before executing. For example:

const invokeAnything = (callback: unknown): void => {
  if (typeof callback === 'function') {
    callback();
  }
  if (typeof callback === 'number') {
    console.log(callback);
  }
  if (typeof callback === 'string') {
    console.log(callback.toUpperCase());
  }
};

// Usage
invokeAnything('typescript'); // Result: TYPESCRIPT

Record for basic object

Probably, nearly every JavaScript developer at some time has used an object as a map-like collection. However, with strict types, it may not be that obvious how to type this. So, you may use interface, but this way you can’t add anything to the object. Then, you need to think about using Record.

The definition:

type Record<K extends keyof any, T> = {
  [P in K]: T;
};

And the usage:

// Usage
const dict: Record<string, number> = {};
dict.a = 1;
dict.b = 'a'; // TypeError: "a" is not assignable to type number

let obj: Record<string, number>;
obj = {
  a: 1,
  b: 2
};

As you can see, it means that the developer can enter any key, but the value has to be of a specific type.

Conclusion

The TypeScript compiler is so powerful. There are so many things we can do with it.

any type can be avoided with more advanced technics such as interface, type intersection, and the use of generics, etc.

Hope you like it! Enjoy TypeScript and make the code without any!

Author: Anh Nguyen

Related Blog

prd-thumb-draft-product

Software Development

+0

    TypeScript And “Any” Type

    Table of contents What is TypeScript?Basic typingWhat is TypeScript any type?Why does TypeScript provide any? TypeScript is a strongly typed programming language that builds on JavaScript, giving you a better ability to detect errors and describe your code. But sometimes you don't know the exact type of value that you're using because it comes from user input or a third-party API. In this case, you want to skip the type checking and allow the value to pass through the compile check. The TypeScript any type is the perfect solution for you because if you use it, the TypeScript compiler will not complain about the type issue. This blog will help you understand the any type in TypeScript but before doing that, let's begin with some basic concepts first! What is TypeScript? TypeScript checks a program for errors before execution, and does so based on the kinds of values, it’s a static type checker. Superset of JavaScript TypeScript is a language that is a superset of JavaScript: JS syntax is therefore legal TS. However, TypeScript is a typed superset, meaning that it adds rules about how different kinds of values can be used. Runtime Behavior TypeScript is also a programming language that preserves the runtime behavior of JavaScript. This means that if you move code from JavaScript to TypeScript, it is guaranteed to run the same way, even if TypeScript thinks that the code has type errors. Erased Types Roughly speaking, once TypeScript’s compiler is done with checking your code, it erases the types to produce the resulting compiled code. This means that once your code is compiled, the resulting plain JS code has no type information. An easy way of understanding TypeScript A languageA superset of JavaScriptPreserver the runtime behavior of JavaScriptType checker layer JavaScript + Types = TypeScript Basic typing Type annotations TypeScript uses type annotations to explicitly specify types for identifiers such as variables, functions, objects, etc. // Syntax : type Once an identifier is annotated with a type, it can be used as that type only. If the identifier is used as a different type, the TypeScript compiler will issue an error. let counter: number; counter = 1; counter = 'Hello'; // Error: Type '"Hello"' is not assignable to type 'number'. The following shows other examples of type annotations: let name: string = 'John'; let age: number = 25; let active: boolean = true; // Array let names: string[] = ['John', 'Jane', 'Peter', 'David', 'Mary']; // Object let person: { name: string; age: number }; person = { name: 'John', age: 25 }; // Valid // Function let sayHello : (name: string) => string; sayHello = (name: string) => { return `Hello ${name}`; }; Type inference Type inference describes where and how TypeScript infers types when you don’t explicitly annotate them. For example: // Annotations let counter: number; // Inference: TypeScript will infer the type the `counter` to be `number` let counter = 1; Likewise, when you assign a function parameter a value, TypeScript infers the type of the parameter to the type of the default value. For example: // TypeScript infers type of the `max` parameter to be `number` const setCounter = (max = 100) => { // ... } Similarly, TypeScript infers the return type to the type of the return value: const increment = (counter: number) => { return counter++; } // It is the same as: const increment = (counter: number) : number => { return counter++; } The following shows other examples of type inference: const items = [0, 1, null, 'Hi']; // (number | string)[] const mixArr = [new Date(), new RegExp('\d+')]; // (RegExp | Date)[] const increase = (counter: number, max = 100) => { return counter++; }; // (counter: number, max?: number) => number Contextual typing TypeScript uses the locations of variables to infer their types. This mechanism is known as contextual typing. For example: document.addEventListener('click', (event) => { console.log(event.button); // Valid }); In this example, TypeScript knows that the event the parameter is an instance of MouseEvent because of the click event. However, when you change the click event to the scroll the event, TypeScript will issue an error: document.addEventListener('scroll', (event) => { console.log(event.button); // Compile error }); // Property 'button' does not exist on type 'Event'. TypeScript knows that the event in this case, is an instance of UIEvent, not a MouseEvent. And UIEvent does not have the button property, therefore, TypeScript throws an error. Other examples about contextual typing // Array members const names = ['John', 'Jane', 'Peter', 'David', 'Mary']; // string[] names.map(name => name.toUpperCase()); // (name: string) => string // Type assertions const myCanvas = document.getElementById('main-canvas') as HTMLCanvasElement; Type inference vs Type annotations Type inferenceType annotationsTypeScript guesses the typeYou explicitly tell TypeScript the type What exactly is TypeScript any? When you don’t explicitly annotate and TypeScript can't infer exactly the type, that means you declare a variable without specifying a type, TypeScript assumes that you use the any type. This practice is called implicit typing. For example: let result; // Variable 'result' implicitly has an 'any' type. So, what exactly is any? TypeScript any is a special type that you can use whenever you don't want a particular value to cause type-checking errors. That means, the TypeScript compiler doesn't complain or issue any error. When a value is of type any, you can access any properties of it, call it like a function, assign it to (or from) a value of any type, or pretty much anything else that’s syntactically legal: let obj: any = { x: 0 }; // None of the following lines of code will throw compiler errors. // Using `any` disables all further type checking, and it is assumed // you know the environment better than TypeScript. obj.foo(); obj(); obj.bar = 100; obj = 'hello'; const n: number = obj; Looking back at an easier to understand any: A special typeSkip/Disable type-checkingTypeScript doesn't complain any or issue any errorDefault implicit typing is any. Note that to disable implicit typing to the any type, you change the noImplicitAny option in the tsconfig.json file to true. Read more noImplicitAny Why does TypeScript provide any type? As described above, while TypeScript is a type checker, any type tell TypeScript to skip/disable type-checking. Whether TypeScript has made a mistake here and why it provides any type? In fact, sometimes the developer can't determine the type of value or can't determine the return value from the 3rd party. Most of cases they use any the type or use implicit typing as any. So they seem to think that TypeScript provides any to do those things. So, is that the root reason that TypeScript provides any? Actually, I think there is a more compelling reason for TypeScript providing any that the any type provides you with a way to work with the existing JavaScript codebase. It allows you to gradually opt-in and opt out of type checking during compilation. Therefore, you can use the any type for migrating a JavaScript project over to TypeScript. Conclusion TypeScript is a Type checker layer. The TypeScript any type allows you to store a value of any type. It instructs the compiler to skip type-checking. Use the any type to store a value when you migrate a JavaScript project over to a TypeScript project. In the next blog, I will show you more about the harmful effects of any and how to avoid them. Hope you like it! See you in the next blog! Reference TypeScript handbookTypeScript tutorial Author: Anh Nguyen

    07/09/2022

    459

    Software Development

    +0

      07/09/2022

      459

      TypeScript And “Any” Type

      What is middleware integration

      Online-Merge-Offline Retail

      Software Development

      +0

        What is Middleware Integration for CDI? | Benefits and Examples

        In the last article, we've discussed Customer Data Integration (CDI) and its important role for OMO Retail. This article will continue to dig deeper into a common type of CDI. Middleware integration is a powerful and flexible solution for CDI, particularly suitable for complex, real-time, and scalable integration needs. Check out SupremeTech's success case studies in building a middleware for an online-merge-offline retail corporation in Japan. What is Middleware Integration? Middleware integration in CDI involves using middleware software to connect and facilitate the exchange of data between different systems, applications, and databases. Middleware acts as an intermediary layer. After successfully built, it ensures smooth communication and data flow without requiring direct connections between the integrated systems. It allows different systems to work together seamlessly. Features of Middleware Integration Connectivity: Middleware provides connectors and adapters to link various systems, regardless of their platforms or technologies. By using middleware, retail businesses do not need to waste time syncing the existing systems of different sales platforms. However, they can still make use of the synchronized database across sales channels to serve customers better. Data Transformation: Middleware can transform data formats and structures to ensure compatibility between different systems. Orchestration: Middleware solutions often include workflow and process orchestration capabilities to manage and automate complex data integration tasks. Scalability: Middleware can handle varying volumes of data and scale according to the business’s needs. We have used middleware to bridge the existing offline system and the online store of a retail giant in Japan with millions of customers. Security: Middleware ensures secure data exchange, protecting sensitive customer information during the integration process. Nowadays, data is considered the capital resource of a business. Securing customer data, therefore, is the utmost priority every business owner concerns. Monitoring and Management: Middleware typically offers tools for monitoring data flows, managing integrations, and troubleshooting issues. Examples of Middleware Solutions Apart from a custom middleware, there are several other handy tools when it comes to a bridge software. MuleSoft Anypoint Platform MuleSoft provides a comprehensive integration platform that enables the connection of any application, data, or device with APIs. It supports both on-premises and cloud integrations. Its main features include API management, data transformation, real-time analytics, and pre-built connectors for various systems. Dell Boomi Boomi offers a cloud-based integration platform as a service (iPaaS) that connects applications and automates workflows across hybrid IT environments. Dell Boomi's highlight features are drag-and-drop interface, pre-built connectors, real-time data synchronization, and extensive support for various protocols. Oracle Integration Cloud Oracle Integration Cloud offers a comprehensive solution for integrating applications and data across on-premises and cloud environments. It offers a wide range of features, including but not limited to pre-built adapters, process automation, visual development tools, and robust analytics. Microsoft Azure Logic Apps Azure Logic Apps is a cloud service that automates and orchestrates tasks and workflows by integrating applications, data, and services across organizations. The main functionalities of MS Azure include extensive integration capabilities, built-in connectors, scalability, and flexibility in designing workflows. Benefits of Middleware Integration Middleware integration offers many benefits for businesses. It ensures seamless communication between different systems and enhances operational efficiency. Middleware provides real-time data availability and supports various integration patterns and workflows. Thus, it is adaptable to evolving business needs. Moreover, it transforms data to ensure system compatibility. It also centralizes management, simplifying monitoring and troubleshooting. Additionally, it enhances security by protecting sensitive data during exchanges. Overall, middleware integration improves decision-making and customer experiences. If you need a custom middleware for your unique business, book a free consultation with us! Providing bridging solutions for online-merge-offline retail businesses is one of SupremeTech's best-selling services. Not only do we have abundant experiences but we also implement fast and cost-efficiently. Let us know your current problems and we will tackle them down together!

        15/07/2024

        78

        Online-Merge-Offline Retail

        +1

        • Software Development

        15/07/2024

        78

        What is Middleware Integration for CDI? | Benefits and Examples

        what is customer data integration and why it is important for omo retail

        Knowledge

        Online-Merge-Offline Retail

        +0

          What is Customer Data Integration (CDI) and why it is important for OMO retail?

          Hi business operators, having a unified view of customer base across various channels can make all the difference. By leveraging customer data integration, businesses can improve customer satisfaction, boost sales, and stay ahead in a rapidly evolving marketplace. This procedure is even more important for OMO retail because of the complexity of customer data the business needs to handle across online and offline sales channels. In this article, we will give answer to the question What is Customer Data Integration and its role for OMO retail. What is Customer Data Integration (CDI)? In short, customer data integration (CDI) involves consolidating information from in-store purchases, online transactions, social media interactions, and more into a single database. This streamlined approach not only enhances customer experiences by enabling personalized marketing and efficient service. It also provides valuable insights that drive strategic decision-making. Most Common Types of Customer Data Integration Customer Data Integration (CDI) can be categorized into several types based on the methods of integration, data sources, and the technology used. Here are some primary types of customer data integration: Batch Data Integration The first type involves collecting and processing data at scheduled intervals rather than in real-time. This method is commonly used for large volumes of data that do not require immediate processing. Data is extracted from various sources, transformed into a consistent format, and then loaded into a target database or data warehouse during off-peak hours to minimize system impact. This type is ideal for routine tasks such as nightly data backups, end-of-day transaction processing, and periodic data synchronization between systems. Key benefits of Batch Data Integration Efficient for processing large datasetsReduces load on systems during business hoursSimplifies data management by handling updates in bulk. Real-Time Data Integration The second type involves integrating data during generation, providing up-to-the-minute information. This method is essential for applications that require immediate data updates and insights. Data is captured and transmitted instantly from various sources to a central system using real-time processing technologies such as message queues, streaming platforms, or APIs. Real-time integration is crucial for applications like live customer support, fraud detection, personalized marketing, and dynamic pricing. Key benefits of Real-Time Data Integration Ensures timely and accurate data availabilityEnhances decision-making with current dataImproves customer experience by enabling immediate responses and interactions API-Based Data Integration The third type, API-based data integration, uses Application Programming Interfaces (APIs) to enable data sharing and integration between different systems and applications. This method supports both real-time and on-demand data exchanges. APIs allow applications to communicate and exchange data directly. Developers can create, manage, and consume APIs to facilitate seamless data flow between disparate systems. API-based integration is widely used for connecting cloud services, integrating third-party applications, enabling mobile apps to access backend data, and synchronizing data between enterprise systems. Key benefits of API-Based Data Integration Provides flexibility and scalabilitySupports real-time data accessSimplifies integration with various systems and platformsAllows for modular and maintainable integration solutions In general, each type of customer data integration has its own advantages and use cases, and organizations often use a combination of these methods to meet their specific needs and goals. Key components of Customer Data Integration (CDI) Data Collection In the early stage of business, customer data is fragmented and not well-synchronized across sales platforms. It's stored in the database of each sale channel and those channels operate independently. Therefore, the first component of CDI process is to gather the pieces of information. Customer data includes transaction records, social media interactions, customer service interactions, loyalty programs, website visits, and mobile app usage. Data Cleaning and Standardization Then, the second thing is ensuring that the data collected is accurate, complete, and consistent. This involves removing duplicates, correcting errors, filling in missing values, and standardizing data formats. Data Integration Data integration involves merging data from disparate sources into a unified database or data warehouse. This involves using data integration tools and technologies that can handle diverse data formats and large volumes of data. Data Storage and Enrichment Storing the integrated data in a centralized repository, such as a data warehouse or a customer relationship management (CRM) system, to facilitate easy access and analysis. Enhancing the integrated data by adding additional information, such as demographic details, psychographic data, and third-party data, to gain a more complete view of the customer. Data Analysis and Insights This component acts as the foundation of data-based decision making for business. From a huge amount of organized data, data readers can uncover patterns, trends, and insights about customer behavior, preferences, and needs. This can involve using analytics tools and techniques such as machine learning, data mining, and predictive analytics. Customer Segmentation Dividing the customer base into distinct segments based on characteristics such as demographics, purchasing behavior, and preferences. This enables more targeted marketing and personalized customer interactions. Why Customer Data Integration is crucial for Online-Merge-Offline business Customer Data Integration is particularly important for Online-Merge-Offline (OMO) retail because it helps to create a seamless and cohesive shopping experience. Here are key reasons why CDI is essential for OMO retail: Unified Customer Experience First and foremost, CDI ensures the consolidation of customer information from online and offline channels. Customers often switch between online and offline channels during their shopping journey. Regardless of that, they will experience the consistent care whether he is shopping in-store, online, or through a mobile app. CDI helps track these transitions seamlessly. Improved Inventory Management Secondly, CDI provides real-time insights into inventory levels across all channels, helping retailers manage stock more efficiently and meet customer demand promptly. Data-Driven Decision Making By integrating data from both online and offline sources, retailers can gain a holistic view of customer behavior and preferences, enabling better decision-making. Integrated data allows for the analysis of trends and patterns across all channels, informing strategies for marketing, product development, and sales. Conclusion In conclusion, Customer Data Integration (CDI) is a vital strategy for businesses seeking to optimize their operations, enhance customer experiences, and drive growth in today’s competitive market. For retailers, particularly those operating in the F&B sector and OMO environments, the importance of CDI cannot be overlooked. It not only ensures consistency and accuracy across multiple channels but also empowers businesses to respond swiftly to customer needs and market trends. Effective CDI enhances operational efficiency, optimizes inventory management, and supports the development of targeted marketing strategies. Ultimately, CDI leads to increased customer satisfaction and loyalty. Embracing CDI is not just about technology implementation; it’s about creating a customer-centric approach that aligns with the dynamic landscape of modern commerce. SupremeTech has experience in handling data integration for businesses with millions of customers. If you're looking for integration services for large-scale system, book a free consultation with us!

          11/07/2024

          112

          Knowledge

          +1

          • Online-Merge-Offline Retail

          11/07/2024

          112

          What is Customer Data Integration (CDI) and why it is important for OMO retail?

          what is react native tab view

          Knowledge

          Software Development

          +0

            An Overview of React Native Tab View

            Hi tech fellows, it's been a while since our last blog. June is usually among the busiest times of the year as we spent time reviewing and planning for the second half of the year. With all the exciting plans ahead, we hope the rest of 2024 will be both challenging and inspiring. We'll keep you posted in the upcoming articles. But for now, let's dive in the next blog series about React Native Tab View. An Overview of React Native Tab View What is React Native Tab View? React Native Tab View is a powerful component for creating tabbed interfaces in React Native applications. It provides a highly customizable and performant solution for adding tab navigation, which is a common requirement in mobile apps. Here's an overview of its key features and components. Key Features of React Native Tab View Smooth Transitions refers to the seamless and fluid animation that occurs when switching between different tabs. This feature offers smooth and customizable transitions between tabs, enhancing user experience. Customization provides highly customizable solutions with support for styling tabs and the tab bar, allowing developers to match the look and feel of their application. Swipeable Tabs allows users to swipe between tabs, which is a common and intuitive navigation pattern on mobile devices. Lazy Loading supports lazy loading of tab content, which can improve performance by only rendering the tab content when it becomes active. This feature is crucial for apps that prioritize high performance and loading speed. Integration with React Navigation can be easily integrated with React Navigation, providing a seamless navigation experience within the app. Accessibility includes all kinds of accessibility-support features. Key Components of React Native Tab View TabView: The main component that holds the tab navigator. It manages the state and renders the appropriate tab content based on the current index. TabBar: A customizable tab bar component that displays the tab labels and handles the user interaction for changing tabs. TabBarIndicator: A component that renders an indicator under the currently active tab, providing visual feedback to the user. SceneMap: A utility function for mapping routes to their corresponding components. It helps in defining the content for each tab. Basic Usage Example import * as React from 'react'; import { View, Text, StyleSheet } from 'react-native'; import { TabView, SceneMap } from 'react-native-tab-view'; const FirstRoute = () => ( <View style={[styles.scene, { backgroundColor: '#ff4081' }]}> <Text>First Tab</Text> </View> ); const SecondRoute = () => ( <View style={[styles.scene, { backgroundColor: '#673ab7' }]}> <Text>Second Tab</Text> </View> ); export default function TabViewExample() { const [index, setIndex] = React.useState(0); const [routes] = React.useState([ { key: 'first', title: 'First' }, { key: 'second', title: 'Second' }, ]); const renderScene = SceneMap({ first: FirstRoute, second: SecondRoute, }); return ( <TabView navigationState={{ index, routes }} renderScene={renderScene} onIndexChange={setIndex} initialLayout={{ width: Dimensions.get('window').width }} /> ); } const styles = StyleSheet.create({ scene: { flex: 1, justifyContent: 'center', alignItems: 'center', }, }); Customization Tab View can be customized extensively through props and styles. You can style the tab bar, change the indicator color, customize the transition animations, and more. Here are a few common customizations: Tab Bar Styling renderTabBar={props => ( <TabBar {...props} indicatorStyle={{ backgroundColor: 'blue' }} style={{ backgroundColor: 'white' }} labelStyle={{ color: 'black' }} /> )} Custom Transitions renderScene={SceneMap({ first: FirstRoute, second: SecondRoute, })} transitionSpec={{ duration: 250, easing: Easing.out(Easing.exp), timing: Animated.timing, }} Conclusion React Native Tab View is a versatile and efficient component for implementing tab navigation in mobile apps. Its flexibility, ease of integration, and support for various customizations make it a popular choice among React Native developers. Whether you need basic tab functionality or advanced features like lazy loading and custom transitions, it provides the tools to create a polished and user-friendly tabbed interface. Contact us if you want an optimized native apps for your company!

            10/07/2024

            74

            Knowledge

            +1

            • Software Development

            10/07/2024

            74

            An Overview of React Native Tab View

            Customize software background

            Want to customize a software for your business?

            Meet with us! Schedule a meeting with us!