关于TypeScript函数参数校验时机、契约合规性及命名合理性的技术咨询
First, let's recap the original function we're discussing:
const getUserInitials = (user: IUser | null) => { if (!user) { return ""; } return (user.FirstName.charAt(0) + user.LastName.charAt(0)).toUpperCase(); }
1. Is your understanding about contract violation and moving parameter validation upstream correct?
Absolutely, you're 100% right here. Here's why:
- The function's implicit contract (shaped by its name) is to return a user's initials. Returning an empty string when passed
nullbreaks this contract—an empty string isn't a valid representation of user initials. - TypeScript's type system exists to enforce valid inputs, so we should lean into it. By changing the parameter type to
IUser(removing the| null), you make the function's requirements explicit: it only accepts valid, existing user objects. - Moving null checks to the caller is the right approach because the caller is best positioned to handle the "no user" scenario (e.g., showing a placeholder avatar, redirecting to a login page, etc.). This keeps your initials function focused on its single, clear responsibility: generating initials from a valid user.
2. Are there valid cases for internal parameter validation (beyond business rules) in statically typed languages?
Yes, there are several scenarios where internal validation makes sense even when the type system enforces basic input types:
- Type system gaps: Your
IUsertype might defineFirstNameandLastNameasstring, but it can't enforce those strings are non-empty. If an empty name would break the function (e.g.,charAt(0)on an empty string returns an empty string), adding a check inside the function (and throwing an error or returning a safe default) is reasonable—this guards against invalid state the type system can't express. - Public API robustness: If your function is part of a public library or API that might be called from untyped JavaScript (or code that bypasses TypeScript checks), internal validation ensures your function doesn't break unexpectedly. Even if your type says
IUser, external callers might pass objects missing required properties. - Untrusted data sources: If the
IUserobject comes from an external system (like an API response), even if you've typed it asIUser, there's a chance the actual data doesn't match the schema. Adding checks inside the function catches these inconsistencies early, before they cause further issues.
3. Is your judgment about the redundant "user" in the function name correct?
This is a solid call. Since the parameter is explicitly typed as IUser, the function name doesn't need to repeat "user" to convey its purpose. getInitials is concise and clear—anyone reading the code will immediately understand it's generating initials from the user object passed to it.
This follows clean code principles: avoid redundant information in names when the context (here, the parameter type) already provides that context. For example, getFullName(user: IUser) is better than getUserFullName for the exact same reason.
内容的提问来源于stack exchange,提问作者john doe

