5 Pro Tips from a Staff Engineer’s TypeScript Code!

5 Pro Tips from a Staff Engineer’s TypeScript Code!I recently worked on a project with a colleague who is a Staff Software Engineer and I noticed several techniques in his TypeScript coding style that stood out to me. These practices clearly highlighte…


This content originally appeared on Level Up Coding - Medium and was authored by Atul Anand

5 Pro Tips from a Staff Engineer’s TypeScript Code!

I recently worked on a project with a colleague who is a Staff Software Engineer and I noticed several techniques in his TypeScript coding style that stood out to me. These practices clearly highlighted the difference between, how a newbie or an intermediate developer and an experienced professional approaches coding.

Working alongside a Staff Engineer can be an enlightening experience. On this particular project, I was excited but also a bit apprehensive about how my code would stack up against someone with such extensive experience. As we delved into the project, it became increasingly clear that several techniques and practices set his code apart. So, I would like to share these 5 key practices I noticed in his TypeScript code that you can use to be a pro coder!

Let’s first see what the code looked like, and then I will point out those 5 key points, and explain a bit about each to make them handy to you the next time you’re writing your code.

Disclaimer: The code I am posting hereinbelow is a simplified version of the actual code with a modified use-case, for public posting and ease of understanding.

A snippet of the code, where the processStudentExamResults function processes a list of students’ exam results to generate summaries, including average scores, unique grades, subject, and comments for passed, failed, and all students, looked something like this:

// Types Definition
type ExamResult = {
subject: string;
score: number;
grade: string;
comments?: string;
};

type Student = {
name: string;
age: number;
results: ExamResult[];
};

type ProcessedStudentDetails = Pick<Student, 'name' | 'age'> & {
averageScore: number;
allGrades: string;
allSubjects: string;
comments: string[];
};

type StudentSummary = {
totalStudents: number;
averageScore: number;
allGrades: string;
allSubjects: string;
comments: string[];
};

const createGradeChecker = (failingGrade: string) => (grade: string) => grade !== failingGrade;
const passingGrade = createGradeChecker('F');

// Function to calculate average score
const calculateAverageScore = (results: ExamResult[]): number =>
results.reduce((acc, result) => acc + result.score, 0) / results.length;

// Generator Function to yield student details one by one
function* studentDetailsGenerator(students: Student[]): Generator<Student> {
for (const student of students) {
yield student;
}
}

// Main function to process students
const processStudentExamResults = (students: Student[]) => {
// Filter passed and failed students
const passedStudents = students.filter(student =>
student.results.some(result => passingGrade(result.grade))
);
const failedStudents = students.filter(student =>
student.results.every(result => !passingGrade(result.grade))
);

// Function to process students details
const processDetails = (students: Student[]): ProcessedStudentDetails[] => {
const studentDetails = studentDetailsGenerator(students);
return Array.from(studentDetails).map((student) => {
const { name, age, results } = student;
const averageScore = calculateAverageScore(results);
const allGrades = [...new Set(results.map((result) => result.grade))].join(', ');
const allSubjects = [...new Set(results.map((result) => result.subject))].join(', ');
const comments = results.flatMap((result) => result.comments || []);

return {
name,
age,
averageScore,
allGrades,
allSubjects,
comments
};
});
};

// Function to summarize results
const summarizeDetails = (processedDetails: ProcessedStudentDetails[]): StudentSummary => {
const totalStudents = processedDetails.length;
const averageScore = calculateAverageScore(processedDetails.map(student => ({
subject: '',
score: student.averageScore,
grade: '',
})));
const allGrades = [...new Set(processedDetails.flatMap((student) => student.allGrades.split(', ')))].join(', ');
const allSubjects = [...new Set(processedDetails.flatMap((student) => student.allSubjects.split(', ')))].join(', ');
const comments = processedDetails.flatMap((student) => student.comments);

return {
totalStudents,
averageScore,
allGrades,
allSubjects,
comments
};
};

const passedDetails = processDetails(passedStudents);
const failedDetails = processDetails(failedStudents);
const allDetails = processDetails(students);

return {
passedSummary: summarizeDetails(passedDetails),
failedSummary: summarizeDetails(failedDetails),
allSummary: summarizeDetails(allDetails)
};
};

And, when calling this function with sample data, it looked something like this:

// Sample Students Input
const students: Student[] = [
{
name: "Alice",
age: 20,
results: [
{ subject: "Math", score: 85, grade: "A", comments: "Excellent" },
{ subject: "English", score: 78, grade: "B", comments: "Good" },
{ subject: "Science", score: 92, grade: "A", comments: "Outstanding" }
]
},
{
name: "Bob",
age: 22,
results: [
{ subject: "Math", score: 65, grade: "C", comments: "Needs Improvement" },
{ subject: "English", score: 88, grade: "A", comments: "Very Good" },
{ subject: "Science", score: 75, grade: "B", comments: "Good" }
]
},
{
name: "Charlie",
age: 21,
results: [
{ subject: "Math", score: 50, grade: "F", comments: "Failed" },
{ subject: "English", score: 45, grade: "F", comments: "Failed" },
{ subject: "Science", score: 55, grade: "F", comments: "Failed" }
]
}
];

// Calling the function
const processedStudentSummary = processStudentExamResults(students);
console.info(processedStudentSummary);

The Output for the given input data returned a summary of passed students, failed students, and overall performance:

{
passedSummary: {
totalStudents: 2,
averageScore: 80.5,
allGrades: 'A, B, C',
allSubjects: 'Math, English, Science',
comments: [
'Excellent',
'Good',
'Outstanding',
'Needs Improvement',
'Very Good',
'Good'
]
},
failedSummary: {
totalStudents: 1,
averageScore: 50,
allGrades: 'F',
allSubjects: 'Math, English, Science',
comments: [ 'Failed', 'Failed', 'Failed' ]
},
allSummary: {
totalStudents: 3,
averageScore: 70.33333333333333,
allGrades: 'A, B, C, F',
allSubjects: 'Math, English, Science',
comments: [
'Excellent',
'Good',
'Outstanding',
'Needs Improvement',
'Very Good',
'Good',
'Failed',
'Failed',
'Failed'
]
}
}

Now that you have seen the code, it might appear a bit overwhelming at first sight. But, let me walk you through it and then I bet, it will be a breeze for you by the end.

1. Extensive use of “Higher Order Functions”

The very first thing that I noticed is that some functions like map, reduce, filter, some, every, etc., are heavily used in the code. These functions are called Higher Order Functions, often referred to as HOFs.

These functions take others functions as arguments or return functions as their result. This practice is prevalent in functional programming and helps develop more abstract, reusable code. Some places to point out where HOFs are used in the code are:

// 'reduce' HOF in calculating average score
const calculateAverageScore = (results: ExamResult[]): number =>
results.reduce((acc, result) => acc + result.score, 0) / results.length;
// 'some' and 'every' HOFs in filtering students
const passedStudents = students.filter(student =>
student.results.some(result => passingGrade(result.grade))
);
const failedStudents = students.filter(student =>
student.results.every(result => !passingGrade(result.grade))
);
// 'map' and 'flatMap' HOFs in parsing details
const allGrades = [...new Set(results.map((result) => result.grade))].join(', ');
const allSubjects = [...new Set(results.map((result) => result.subject))].join(', ');
const comments = results.flatMap((result) => result.comments || []);

2. Practical use of the “Generator function”

I’m not sure, but did you notice the function* studentDetailsGenerator() in the code?

// generator function to yield one student details at a time
function* studentDetailsGenerator(students: Student[]): Generator<Student> {
for (const student of students) {
yield student;
}
}

...
// getting each student details, and processing
const studentDetails = studentDetailsGenerator(students);
...

I used to wonder who would actually use yield in real code when you can achieve similar outcomes in other fundamental ways. As naive as it may sound, it is indeed used in practical applications!

So, the generator function produces values on-demand, pausing the execution, retaining the state, and resuming after the previous yield run. This approach is particularly useful in the case of lazy evaluation of values when the dataset is quite large. It is also used when implementing custom iterators, and in developing readable asynchronous code.

3. Spread Operators and Object Destructuring

The spread operator ... and object destructuring are modern Javascript techniques, that allow concise and expressive manipulation of objects and arrays. I really love the way these make the code clean and readable, and handling data easier. Look at the elegant way to achieve this in just a few lines of code:

// spread operator with Set used to create list of unique grades and subject
const allGrades = [...new Set(results.map((result) => result.grade))].join(', ');
const allSubjects = [...new Set(results.map((result) => result.subject))].join(', ');

// object destructuring to get attributes of student
const { name, age, results } = student;

An inexperienced or beginner-level developer, without much knowledge of these techniques, might write the same code in this way:

// Using loops and arrays to create a list of unique grades and subjects
const allGradesArray = [];
const allSubjectsArray = [];

for (let i = 0; i < results.length; i++) {
const grade = results[i].grade;
const subject = results[i].subject;

if (!allGradesArray.includes(grade)) {
allGradesArray.push(grade);
}

if (!allSubjectsArray.includes(subject)) {
allSubjectsArray.push(subject);
}
}

const allGrades = allGradesArray.join(', ');
const allSubjects = allSubjectsArray.join(', ');

// Accessing attributes of student without destructuring
const name = student.name;
const age = student.age;
const results = student.results;

This will also give the same result, but it’s all about efficiency in terms of LOC (Lines of Code) and performance. All of this comes with experience.
But, don’t worry! I have got you covered with such blogs ;)

4. Advanced Type Definitions

As you might already know, TypeScript is all about types. So, it becomes really necessary to know some advanced ways to define and handle types to enhance our code robustness and maintainability.

// Types Definition
type ExamResult = {
subject: string;
score: number;
grade: string;
comments?: string;
};

...

type ProcessedStudentDetails = Pick<Student, 'name' | 'age'> & {
averageScore: number;
allGrades: string;
allSubjects: string;
comments: string[];
};

...

In the code, every complex type like ProcessedStudentDetails and ExamResult are defined to provide clear and explicit type definitions, reducing potential errors. That’s really good.

But, what’s even better, is the way the Pick utility type is used to create a new type based on the selected properties from an existing type! Knowing the advanced features of the language is essential as you advance your skills and progress in your Software Engineering career.

5. Use of Currying technique

The code also incorporates the popular currying technique to generate functions to check students’ grades.

// Currying function to generate grade checker base functions
const createGradeChecker = (failingGrade: string) => (grade: string) => grade !== failingGrade;

// Base function to check passing grade
const passingGrade = createGradeChecker('F');

...
// Filtering out failed students
const failedStudents = students.filter(student =>
student.results.every(result => !passingGrade(result.grade))
);

...

In the code snippet, you must have seen the createGradeChecker function. For simplicity, only passingGrade function is shown here, which checks and returns if the student has passed or not.
But, similarly, we can define different grades for different contexts, like:

// Check the students with Distinction or Outstanding grade
const distinctionGrade = createGradeChecker('O');

That’s the beauty of currying, as it allows a function to be transformed into a series of functions, each taking a single argument. And, as you saw above, it introduces reusability and readability by allowing functions to be applied partially.

Alright! That’s all for this blog, and I hope, at least 1 or 2 of these techniques will help you to level yourself up if you’ve made till here. Embracing these best practices can elevate your coding skills, helping you to produce professional-level code that stands out.

So, the next time you work on a project, consider using these advanced techniques in your code, and impress your code reviewers ;)

Please make sure you give this post 50 claps 👏🏼 and my blog a follow ❤️ if you enjoyed it and want to see more.
Happy Learning! 🚀


5 Pro Tips from a Staff Engineer’s TypeScript Code! was originally published in Level Up Coding on Medium, where people are continuing the conversation by highlighting and responding to this story.


This content originally appeared on Level Up Coding - Medium and was authored by Atul Anand


Print Share Comment Cite Upload Translate Updates
APA

Atul Anand | Sciencx (2024-07-28T17:02:48+00:00) 5 Pro Tips from a Staff Engineer’s TypeScript Code!. Retrieved from https://www.scien.cx/2024/07/28/5-pro-tips-from-a-staff-engineers-typescript-code/

MLA
" » 5 Pro Tips from a Staff Engineer’s TypeScript Code!." Atul Anand | Sciencx - Sunday July 28, 2024, https://www.scien.cx/2024/07/28/5-pro-tips-from-a-staff-engineers-typescript-code/
HARVARD
Atul Anand | Sciencx Sunday July 28, 2024 » 5 Pro Tips from a Staff Engineer’s TypeScript Code!., viewed ,<https://www.scien.cx/2024/07/28/5-pro-tips-from-a-staff-engineers-typescript-code/>
VANCOUVER
Atul Anand | Sciencx - » 5 Pro Tips from a Staff Engineer’s TypeScript Code!. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2024/07/28/5-pro-tips-from-a-staff-engineers-typescript-code/
CHICAGO
" » 5 Pro Tips from a Staff Engineer’s TypeScript Code!." Atul Anand | Sciencx - Accessed . https://www.scien.cx/2024/07/28/5-pro-tips-from-a-staff-engineers-typescript-code/
IEEE
" » 5 Pro Tips from a Staff Engineer’s TypeScript Code!." Atul Anand | Sciencx [Online]. Available: https://www.scien.cx/2024/07/28/5-pro-tips-from-a-staff-engineers-typescript-code/. [Accessed: ]
rf:citation
» 5 Pro Tips from a Staff Engineer’s TypeScript Code! | Atul Anand | Sciencx | https://www.scien.cx/2024/07/28/5-pro-tips-from-a-staff-engineers-typescript-code/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.