```python
def parse_semver(version: str) -> tuple[int, int, int, str | None]:
    parts = version.split('-')
    
    if len(parts) > 2:
        raise ValueError("too many parts")
    
    major, minor, patch = map(int, parts[0].split('.'))
    
    prerelease = None
    if len(parts) == 2:
        prerelease = parts[1]
        
        # Validate prerelease part
        if not prerelease or not prerelease.replace('-', '').replace('.', '').isalnum():
            raise ValueError("invalid prerelease")
    
    return (major, minor, patch, prerelease)
```